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

コマンドラインツールを作る際にオプションやサブコマンドを付けるのに苦労したり、何か作ったはいいもののいい感じに人に共有する方法が分からず自分が使うだけで終わってしまうといったことはないでしょうか?
今は、コマンドラインツールを作りやすくするためのフレームワークなどがあるので、今回はそれを用いて実際にコマンドラインツールを作成して、配布するまでの手順について書いていこうと思います。
コマンドラインツールの作成
コマンドラインツールは各環境ですぐに実行できるようにバイナリファイルで作成したいたいということでコンパイル型言語を使いたいので、今回はコンパイル型言語でシンプルな言語仕様である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ストック委譲されている記事を取得する機能を追加します。
この機能のコードは以前に投稿した記事で使ったコードを使うので、このコードが気になった方は以下の記事を読んでみてください。
以前使ったコードをパッケージ化して、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のリリース機能を是非試してみてはいかがでしょうか。