簡単にできる!Goで書いたCLIツールを配布する方法
どうも、id:nukosukeです。
さっそくですがみなさん、パッとCLIツールを作るとなったらやっぱりGoですか?
Goですよね?
でも、「作ったツールを配布したい」となったときにちょっと困ったことがあります。
- 開発マシンにGoの環境を作ってる人が少なかった (構築方法説明するのは面倒)
- Go環境入れてもらったもののバージョンが違ってビルドで死ぬ
- depやvgoのようなバージョンマネージャを使ってなくて、依存ライブラリがアップグレードしててビルドで死ぬ
とかとか。
前のEmacsの記事でも同じようなことを言いましたが、環境再現性ってやっぱり大事ですよね。この辺りの煩雑さは周辺ツールではなくGo本体のバージョンアップで今後対応が進むようですが、そもそもツールを使うだけの人にビルド環境を用意させるのはどうなんでしょう。バイナリで配布すれば面倒な環境構築も必要ないわけでして。
というわけで、今回の記事ではGoで書いたツールのテストと配布 (CI・CD*1 )環境をサクッと作ります。
要件
- お金をかけずに誰でもダウンロードできる場所に置いておきたい
- gitでバージョンタグを打ったら自動でビルドして配布用のアーカイブがアップロードされるようにしたい
- OSやCPUアーキテクチャごとにクロスコンパイルしたバージョンを用意したい
使用するツール・サービス
GitHub
言わずと知れたGitホスティングサービスですね。今回はツールのバイナリ配布のためにreleaseという機能を使います。releaseでは特定のタグやリビジョンに対してソースコードのアーカイブ(.zip, .tar.gz)を自動で作成してくれます。加えて、任意のファイルを追加することができますのでここにバイナリをzipに固めたものをアップロードしていきます。
TravisCI
OSS開発においてお金をかけずにCIを導入したいというときにTravisCIとCircleCIが選択肢に上がるかと思います。(他にもたくさんCIサービスがありますね。おすすめがあればぜひ教えてください)
CI | ビルド環境 | id:nukosukeの所感 |
---|---|---|
TravisCI | VM | あんまり複雑なことを考えずサクッと導入したい人向け。TravisCIがサポートしている機能だけで要件を満たせるなら導入がすごくラク。GitHub releaseを自動で作る機能など便利機能が満載。 |
CircleCI | コンテナ(v2.0より) | テスト環境をカスタマイズしてステップごとに色々やりたい人向け。Dockerイメージを使用してとことんカスタマイズできるため複雑な要件の場合はこちらを選択した方がいい。ステップごとにイメージの切り替えが可能。 |
今回はTravisCIの機能で要件を満たせるのでこちらを使ってスピーディーにCI環境を作ります。ちなみに今回は触れませんが、WindowsでテストしたいときはAppVeyorが便利です。
gox
Goは GOOS
, GOARCH
環境変数を指定することで様々なプラットフォーム向けにビルドすることができます。しかし、いちい環境変数を指定してプラットフォームごとにgo build
を繰り返すのは面倒です。一発で複数のプラットフォーム向けのバイナリを作りたいですよね。
はい、そんなときにgoxが便利です。デフォルトではサポートしているOSとアーキテクチャの全組み合わせでバイナリを出力します。ターゲットを絞りたい場合は、OSとアーキテクチャのリストをオプションに渡すとその分だけビルドすることができます。
使用方法の詳細については後ほど見ていきます。
手順
GitHub: Personal Tokenの発行
releaseページにバイナリをアップロードするのに使います。場所が少し分かりづらいのですが、Settings → Developer settings → Personal access tokens から発行できます。
Generate new tokenで発行できたらTravisCIの環境変数に GITHUB_TOKEN
という名前で保存しておきましょう。
この際、Display value in build logがオフになっていることを確認しておきましょう。これがオンになっていると、テストのログに$GITHUB_TOKEN
の中身が表示されてしまうためトークンが漏洩してしまいます。
TravisCI: go testの実行
TravisCIでgo test
を走らせる設定です。この記事の本旨とは関係ないですが、パッケージマネージャにdepを、テストカバレッジの計測にCoverallsを使っています。
# 言語を指定します language: go # テストを回すGoのバージョンをリストで指定します go: - "1.11.x" - master # 依存ライブラリのインストール処理前に走らせる処理を書きます # ここではdepパッケージマネージャとテストカバレッジ計測のためにgoverallsを入れています before_install: - curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh - go get github.com/mattn/goveralls # for profiling coverage # Gopkg.toml, Gopkg.lockをもとに依存ライブラリをインストールします # installを指定しないとデフォルトではgo getが使用されるため、 # 依存ライブラリのアップデートで突然壊れる可能性があります install: - dep ensure # テストを実行します # 2つ目はカバレッジ計測のコマンドです script: - go test -v ./... - goveralls -service=travis-ci
自動テスト(CI)だけが目的であればこれで完了ですが、今回は配布(CD)もやりたいので続いてその設定をやっていきましょう。
TravisCI: goxでクロスコンパイル
先ほどの.travis.yml
にビルドの設定を追記します。デフォルトではgoxはカレントディレクトリに {{.Dir}}_{{.OS}}_{{.Arch}}
(Windowsの場合は.exeがつく) の形式の名前でバイナリを吐き出しますが、これだとLICENSEやREADMEなど同梱したいファイルがある場合に不便です。そこでディレクトリを切ってその中にバイナリを出力するように -output
オプションを指定しています。ターゲットのOSやアーキテクチャはスペース区切りの文字列で渡します。 -osarch="linux/amd64"
のように一緒に指定することもできます。
詳しくは gox -h
で確認してみてください。
before_deploy: # goxをインストール - go get github.com/mitchellh/gox # Linux, macOS, Windowsに対して、 # 386(32bit), amd64(64bit)CPU向けのバイナリをビルドします - gox -os="linux darwin windows" -arch="386 amd64" -output="{{.Dir}}-{{.OS}}-{{.Arch}}/{{.Dir}}" # バイナリを出力したディレクトリをそれぞれzipに固めます - | for os in linux darwin windows; do for arch in 386 amd64; do cp README.md LICENSE terraform-provider-zendesk-$os-$arch zip -r terraform-provider-zendesk-$os-$arch.zip terraform-provider-zendesk-$os-$arch done done
TravisCI: GitHub releaseページにバイナリをアップロード
さらに、GitHub releaseページにバイナリをアップロードする設定を追記します。
deploy: provider: releases api_key: $GITHUB_TOKEN file_glob: true file: terraform-provider-zendesk-*-*.zip skip_cleanup: true # tagが打たれたときのみGo 1.11でビルドしたバイナリをアップロードします on: tags: true go: "1.11.x"
ではさっそくgit tag
でバージョンタグを打ってみます。しばらくして、releaseページに以下のように.zipがアップロードされていたら成功です。
それでは、楽しいGoライフを!
*1:CI = Continuous Integration, CD = Continuous Delivery