Go言語でコマンドラインツールを作成して配布する

Blog Single

コマンドラインツールを作る際にオプションやサブコマンドを付けるのに苦労したり、何か作ったはいいもののいい感じに人に共有する方法が分からず自分が使うだけで終わってしまうといったことはないでしょうか?

今は、コマンドラインツールを作りやすくするためのフレームワークなどがあるので、今回はそれを用いて実際にコマンドラインツールを作成して、配布するまでの手順について書いていこうと思います。

コマンドラインツールの作成

コマンドラインツールは各環境ですぐに実行できるようにバイナリファイルで作成したいたいということでコンパイル型言語を使いたいので、今回はコンパイル型言語でシンプルな言語仕様であるGo言語を使ってコマンドラインツールを作成します。

Go言語のCLIアプリケーションのフレームワークとして様々なものがありますが、今回はシンプルな機能のものを作りたいため、urfave/cliを使います。

サブコマンドも多く高機能なものを作りたい場合は、DockerやHugoなどが採用しているspf13/cobraというのもいいみたいです。

環境

$ go version
go version go1.10.4 darwin/amd64
$ # 環境変数 GOPATH
$ echo $GOPATH
/Users/matsumoto/go

Helloプログラムの作成

urfave/cli インストール

$ go get github.com/urfave/cli

作業ディレクトリは、後ほどGitHubにアップロードすることを想定して、Go言語のパッケージシステムに則るように $GOPATH以下のsrc/github.com/アカウント名という箇所にcli-sampleというディレクトリを作成し、そこにプログラムを作成します。

こちらの環境では/Users/matsumoto/go/src/github.com/fox-kazuki-matsumoto/cli-sampleという形になります。

サンプルプログラムを用いて、Hello friend!と出力するプログラムを実行します。

main.go

package main

import (
  "fmt"
  "log"
  "os"

  "github.com/urfave/cli"
)

func main() {
  app := cli.NewApp()
  app.Name = "greet"
  app.Usage = "fight the loneliness!"
  app.Action = func(c *cli.Context) error {
    fmt.Println("Hello friend!")
    return nil
  }

  err := app.Run(os.Args)
  if err != nil {
    log.Fatal(err)
  }
}

ビルドを行なってバイナリファイルを作成します。

$ go install

$GOPATH/bin直下にバイナリファイルが作成されます。
ここではcli-sampleというファイル名になっているので、それを実行します。

$ cli-sample
Hello friend!

このように出力されました。

機能は少ないですが、まずはこれをアップロードしていきます。

GitHubにアップロード

GitHubにアップロードには、GitHubのサイトから手動で直接ファイルのアップロードもできますが、バイナリファイルを各OS用にビルドして、それぞれアップロードする必要がありますがかなり面倒なので、Goのバイナリを簡単に配布を行えるツールであるGoReleaserを用います。

goreleaser インストール

$ go get github.com/goreleaser/goreleaser

goreleaser用に設定ファイルを作成します。

設定ファイルに生成されるバイナリの名前を指定する箇所がありますが、後でQiitaの記事を検索する機能を作成するので、qiitasearchとしておきます。

.goreleaser.yml

# コンパイルの設定
builds:
# 対象ファイル
- main: main.go
# バイナリの名前
  binary: qiitasearch
  # どのOSようにコンパイルするか
  goos:
  - windows
  - darwin
  - linux
  # どのアーキテクチャ用にコンパイルするか
  goarch:
  - amd64

必要なプログラムと設定ファイルはこれで作成できたので、これに.gitignoreファイルを作成して、GitHubに追加します。

この状態のコードは以下のリンクから見れます。

v1.0.0 fox-kazuki-matsumoto/cli-sample

そして配布を行うために、タグをつけます。

$ git tag v1.0.0
$ git push --tags

goreleaserを使うためにGitHubのアクセストークンの取得が必要なのでGitHub Tokenを持っていない場合は、取得する必要があります。

以下のコマンドで登録しているトークンが表示されていれば、問題ありません。

$ echo $GITHUB_TOKEN

GitHub Tokenの取得

GitHubの設定ページに移動します。

このサイドバーのDeveloper Setting -> Personal access tokensからトークン作成画面に移動し、repoにチェックを入れて作成を行います。

生成されたトークンの文字列を環境変数に設定します。
今後もトークンを使う場合は、.bash_profile等に設定して、シェルを起動し直しても環境変数が設定されるようにしておくと便利です。

環境変数設定

export GITHUB_TOKEN="<<YOUR ACCESS TOKEN>>"

goreleaserでリリース

GitHubトークンを設定したら、コマンドを打って実行します。

$ goreleaser

正常に完了するとGitHubのリポジトリのページのリリースタブに以下のように表示されるので、リリースページに移動します。

このように、リリースができました。

配布したバイナリを実行する

実際にこれをダウンロードします。

OSにあったファイルをダウンロードを行いますが、現在利用しているOSがmacOSなのでcli-sample_1.0.0_darwin_amd64.tar.gzをダウンロードします。

解凍すると以下のように、qiitasearchというバイナリになります。

このバイナリがあるディレクトリから、バイナリを実行します。
バイナリなのでGoのインストールをしていなくても実行できます。

$ qiitasearch
Hello friend!

このように実行されました。

Qiitaの記事を取得する機能を追加

Helloと出力するだけだと機能が物足りないので、Qiitaの過去一週間で30ストック委譲されている記事を取得する機能を追加します。

この機能のコードは以前に投稿した記事で使ったコードを使うので、このコードが気になった方は以下の記事を読んでみてください。

Qiita APIをGo言語で叩いて人気記事を取得する

以前使ったコードをパッケージ化して、main.goを以下のように変えます。

package main

import (
    "log"
    "os"

    "github.com/urfave/cli"
    "github.com/fox-kazuki-matsumoto/cli-sample/api"
)

func main() {
    app := cli.NewApp()
    app.Name = "qiitasearch"
    app.Usage = "search qiita articles"
    app.Action = func(c *cli.Context) error {
        //fmt.Println("Hello friend!")
        api.RunQiitaSearch()
        return nil
    }

    err := app.Run(os.Args)
    if err != nil {
        log.Fatal(err)
    }
}

出来上がったコードはこちら

また、タグをつけてプッシュします。

$ git tag v1.0.1
$ git push --tags

v.1.0.1のリリース

$ goreleaser --rm-dist

バイナリの実行

Qiitaのアクセストークンを設定していない場合は、同じIPアドレスごとに1時間ごとに60回までのリクエストの制限がある(2019/1/11現在)ので、トークンを取得したい場合は以前書いた記事を参考にしてみてください。

バイナリをパスが通った箇所に置いておくと、どこのディレクトリからでも実行できます。

まとめ

今までGitHubのリリース機能を自分で使ったことがありませんでしたが、実際に人に使われるかどうかは置いておいて、自分が作ったCLIツールをリリースして他の人に使ってもらえる状態にするというのはなかなか面白かったです。

GitHubへのリリースは、今回紹介したgoreleaserのようにリリースするためのツールも整っているので、簡単なツールの公開にGitHubのリリース機能を是非試してみてはいかがでしょうか。

Posted by MatsumotoKazuki
PHPやJavaで開発を行っているエンジニア。 LOLというゲームの試合を観戦するのが好きです。

Other Posts: