Travis CI でクロス・コンパイル(GoReleaser 編)
以前「Travis CI でクロス・コンパイル」で mitchellh/gox を使ったクロス・コンパイルと tcnksm/ghr を使った GitHub への deploy 手順を紹介したが,これらをまとめてやってくれる GoReleaser というツールがあるらしい。
- GoReleaser | Deliver Go binaries as fast and easily as possible
- goreleaser/goreleaser: Deliver Go binaries as fast and easily as possible
すでにあるプロジェクトで試すのはどうかと思ったので,まずは以下のデモ用のリポジトリを作って試してみることにした。
ちなみに中身は,このまえついカッとなって作った spiegel-im-spiegel/godump のコードを流用している。 おおっ,役に立ったじゃないか(笑)
GoReleaser の導入
当然ながら GoReleaser 自身は GitHub 上でバイナリを配布しているので,そちらを使うのが手っ取り早い。
ヘルプを見るとこんな感じ。
$ goreleaser help
NAME:
goreleaser - Deliver Go binaries as fast and easily as possible
USAGE:
goreleaser.exe [global options] command [command options] [arguments...]
VERSION:
0.35.5, commit 11fee22a2edf211caec98f2bee97576d3160bdb7, built at 2017-10-25T11:49:23Z
COMMANDS:
init, i generate .goreleaser.yml
help, h Shows a list of commands or help for one command
GLOBAL OPTIONS:
--config FILE, --file FILE, -c FILE, -f FILE Load configuration from FILE (default: ".goreleaser.yml")
--release-notes FILE Load custom release notes from a markdown FILE
--skip-validate Skip all the validations against the release
--skip-publish Skip all publishing pipes of the release
--snapshot Generate an unversioned snapshot release
--rm-dist Remove ./dist before building
--parallelism value, -p value Amount of builds launch in parallel (default: 4)
--debug Enable debug mode
--help, -h show help
--version, -v print the version
GoReleaser では .goreleaser.yml
ファイルでビルドや deploy を制御しているようだ。
goreleaser init
コマンドで雛形を生成してくれるみたいなので試してみる。
$ goreleaser init
• config created; please edit accordingly to your needs file=.goreleaser.yml
作成された .goreleaser.yml
の中身はこんな感じ。
project_name: reldemo
release:
github:
owner: spiegel-im-spiegel
name: reldemo
name_template: '{{.Tag}}'
brew:
commit_author:
name: goreleaserbot
email: goreleaser@carlosbecker.com
install: bin.install "reldemo"
builds:
- goos:
- linux
- darwin
goarch:
- amd64
- "386"
goarm:
- "6"
main: .
ldflags: -s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}}
binary: reldemo
archive:
format: tar.gz
name_template: '{{ .Binary }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{
.Arm }}{{ end }}'
files:
- licence*
- LICENCE*
- license*
- LICENSE*
- readme*
- README*
- changelog*
- CHANGELOG*
snapshot:
name_template: SNAPSHOT-{{ .Commit }}
checksum:
name_template: '{{ .ProjectName }}_{{ .Version }}_checksums.txt'
これをベースにアレンジしていくわけだ。
設定ファイルの調整とクロス・コンパイル
さて,修正した .goreleaser.yml
がこれ。
project_name: reldemo
release:
github:
owner: spiegel-im-spiegel
name: reldemo
builds:
- goos:
- linux
- darwin
- windows
goarch:
- amd64
- "386"
- arm
- arm64
goarm:
- "6"
main: ./cli/reldemo/
ldflags: -s -w -X github.com/spiegel-im-spiegel/reldemo/cli/reldemo/facade.Version={{ .Version }}
binary: reldemo
archive:
format: tar.gz
format_overrides:
- goos: windows
format: zip
name_template: '{{ .Binary }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}'
replacements:
amd64: 64bit
386: 32bit
arm: ARM
arm64: ARM64
darwin: macOS
linux: Linux
windows: Windows
files:
- LICENSE*
- README*
snapshot:
name_template: SNAPSHOT-{{ .Commit }}
checksum:
name_template: '{{ .ProjectName }}_{{ .Version }}_checksums.txt'
主な変更点は以下の通り。
brew
項目はバッサリ捨てた- コンパイル対象の OS に Windows を加えた。更にアーキテクチャに ARM を加えた
ldflags
を現状のものに合わせた- 圧縮フォーマットで Windows の場合は zip 圧縮にした。また名前の置き換えも行った
これで実際にビルドを行ってみる。
$ goreleaser --snapshot --skip-publish
• running goreleaser 0.35.5
• loading config file file=.goreleaser.yml
• publishing disabled in snapshot mode
• SETTING DEFAULTS
• GETTING AND VALIDATING GIT STATE
• releasing v0.0.9, commit 6b96405452b3b9af8817157629fce00acb81564e
• GENERATING CHANGELOG
• skipped reason=not available for snapshots
• LOADING ENVIRONMENT VARIABLES
• skipped reason=publishing is disabled
• CHECKING ./DIST
• BUILDING BINARIES
• building binary=dist\reldemo_SNAPSHOT-6b96405452b3b9af8817157629fce00acb81564e_Linux_ARM64\reldemo
• building binary=dist\reldemo_SNAPSHOT-6b96405452b3b9af8817157629fce00acb81564e_Linux_32bit\reldemo
• building binary=dist\reldemo_SNAPSHOT-6b96405452b3b9af8817157629fce00acb81564e_Linux_64bit\reldemo
• building binary=dist\reldemo_SNAPSHOT-6b96405452b3b9af8817157629fce00acb81564e_Linux_ARMv6\reldemo
• building binary=dist\reldemo_SNAPSHOT-6b96405452b3b9af8817157629fce00acb81564e_macOS_64bit\reldemo
• building binary=dist\reldemo_SNAPSHOT-6b96405452b3b9af8817157629fce00acb81564e_macOS_32bit\reldemo
• building binary=dist\reldemo_SNAPSHOT-6b96405452b3b9af8817157629fce00acb81564e_Windows_64bit\reldemo.exe
• building binary=dist\reldemo_SNAPSHOT-6b96405452b3b9af8817157629fce00acb81564e_Windows_32bit\reldemo.exe
• CREATING ARCHIVES
• creating archive=dist\reldemo_SNAPSHOT-6b96405452b3b9af8817157629fce00acb81564e_Linux_ARM64.tar.gz
• creating archive=dist\reldemo_SNAPSHOT-6b96405452b3b9af8817157629fce00acb81564e_Linux_32bit.tar.gz
• creating archive=dist\reldemo_SNAPSHOT-6b96405452b3b9af8817157629fce00acb81564e_Linux_64bit.tar.gz
• creating archive=dist\reldemo_SNAPSHOT-6b96405452b3b9af8817157629fce00acb81564e_macOS_32bit.tar.gz
• creating archive=dist\reldemo_SNAPSHOT-6b96405452b3b9af8817157629fce00acb81564e_Linux_ARMv6.tar.gz
• creating archive=dist\reldemo_SNAPSHOT-6b96405452b3b9af8817157629fce00acb81564e_Windows_64bit.zip
• creating archive=dist\reldemo_SNAPSHOT-6b96405452b3b9af8817157629fce00acb81564e_Windows_32bit.zip
• creating archive=dist\reldemo_SNAPSHOT-6b96405452b3b9af8817157629fce00acb81564e_macOS_64bit.tar.gz
• new release artifact artifact=reldemo_SNAPSHOT-6b96405452b3b9af8817157629fce00acb81564e_Linux_64bit.tar.gz
• new release artifact artifact=reldemo_SNAPSHOT-6b96405452b3b9af8817157629fce00acb81564e_Windows_64bit.zip
• new release artifact artifact=reldemo_SNAPSHOT-6b96405452b3b9af8817157629fce00acb81564e_Windows_32bit.zip
• new release artifact artifact=reldemo_SNAPSHOT-6b96405452b3b9af8817157629fce00acb81564e_Linux_32bit.tar.gz
• new release artifact artifact=reldemo_SNAPSHOT-6b96405452b3b9af8817157629fce00acb81564e_Linux_ARM64.tar.gz
• new release artifact artifact=reldemo_SNAPSHOT-6b96405452b3b9af8817157629fce00acb81564e_Linux_ARMv6.tar.gz
• new release artifact artifact=reldemo_SNAPSHOT-6b96405452b3b9af8817157629fce00acb81564e_macOS_32bit.tar.gz
• new release artifact artifact=reldemo_SNAPSHOT-6b96405452b3b9af8817157629fce00acb81564e_macOS_64bit.tar.gz
• CREATING LINUX PACKAGES WITH FPM
• skipped reason=no output formats configured
• CREATING LINUX PACKAGES WITH SNAPCRAFT
• skipped reason=no summary nor description were provided
• CALCULATING CHECKSUMS
• checksumming file=reldemo_SNAPSHOT-6b96405452b3b9af8817157629fce00acb81564e_macOS_64bit.tar.gz
• checksumming file=reldemo_SNAPSHOT-6b96405452b3b9af8817157629fce00acb81564e_Linux_32bit.tar.gz
• checksumming file=reldemo_SNAPSHOT-6b96405452b3b9af8817157629fce00acb81564e_Linux_64bit.tar.gz
• checksumming file=reldemo_SNAPSHOT-6b96405452b3b9af8817157629fce00acb81564e_Linux_ARMv6.tar.gz
• checksumming file=reldemo_SNAPSHOT-6b96405452b3b9af8817157629fce00acb81564e_Windows_64bit.zip
• checksumming file=reldemo_SNAPSHOT-6b96405452b3b9af8817157629fce00acb81564e_Windows_32bit.zip
• checksumming file=reldemo_SNAPSHOT-6b96405452b3b9af8817157629fce00acb81564e_Linux_ARM64.tar.gz
• checksumming file=reldemo_SNAPSHOT-6b96405452b3b9af8817157629fce00acb81564e_macOS_32bit.tar.gz
• new release artifact artifact=reldemo_SNAPSHOT-6b96405452b3b9af8817157629fce00acb81564e_checksums.txt
• CREATING DOCKER IMAGES
• skipped reason=docker section is not configured
• RELEASING TO GITHUB
• skipped reason=--skip-publish is set
• CREATING HOMEBREW FORMULA
• skipped reason=--skip-publish is set
• SUCCESS!
これでビルドと(README.md
や LICENSE
ファイルを同梱した)圧縮ファイルの生成までできた。
fpm とか snapcraft とか Homebrew とか Docker イメージとか設定がないのでスキップしてるけど,今回はスルーの方向で。
まだバージョンタグを打ってないので --snapshot
で。
また現時点では GitHub に deploy して欲しくないので --skip-publish
にしている。
OS とアーキテクチャの組み合わせで出来ないものはビルドされないのが分かるだろうか。
実際に生成された実行モジュールを起動してみると
$ reldemo -h
Usage:
reldemo [flags] [binary file]
Flags:
-h, --help help for reldemo
-n, --name string value name (default "dumpList")
-v, --vaersion output version
$ reldemo -v
reldemo SNAPSHOT-6b96405452b3b9af8817157629fce00acb81564e
という感じでバージョン番号にスナップショット情報が入っているのがわかると思う。
なお GoReleaser でビルドする際は全てのファイルがコミットされている必要がある。
したがって .goreleaser.yml
ファイルの調整に手こずるとコミット履歴がアレなことになる。
Travis CI との連携と GitHub への Deploy
「Travis CI でクロス・コンパイル」でも書いたが Travis CI から GitHub へ Deploy するためには GitHub のアクセス・トークンを取得して Travis CI の環境変数としてセットする必要がある。
GitHub のアクセス・トークンは “Settings” の “Developer settings” → “Personal access tokens” のページで取得できる。
repo の権限のみを付けること。 この access token を Travis CI で参照するには, “Settings” の “Environment Variables” でセットすればよい。 Build log にこの access token が表示されないようにすること。
今回の .travis.yml
の内容はこんな感じ1。
language: go
go:
- 1.9.*
env:
- DEP_VERSION="0.4.1"
before_install:
# Download the binary to bin folder in $GOPATH
- curl -L -s https://github.com/golang/dep/releases/download/v${DEP_VERSION}/dep-linux-amd64 -o $GOPATH/bin/dep
# Make the binary executable
- chmod +x $GOPATH/bin/dep
install:
- $GOPATH/bin/dep ensure -v
script:
- go test -v ./...
after_success:
- test -n "$TRAVIS_TAG" && curl -sL https://git.io/goreleaser | bash
最後の行が GoReleaser 起動に関する記述である。
GitHub リポジトリにタグが打たれている場合は $TRAVIS_TAG
にタグの値が入る。
したがってタグがない場合は GoReleaser は起動しない。
https://git.io/goreleaser
は短縮 URL で,中身は goreleaser/get の get
ファイルでシェル・スクリプトになっている。
こんな感じ。
#!/bin/sh
set -e
TAR_FILE="/tmp/goreleaser.tar.gz"
RELEASES_URL="https://github.com/goreleaser/goreleaser/releases"
test -z "$TMPDIR" && TMPDIR="$(mktemp -d)"
last_version() {
curl -sL -o /dev/null -w %{url_effective} "$RELEASES_URL/latest" |
rev |
cut -f1 -d'/'|
rev
}
download() {
test -z "$VERSION" && VERSION="$(last_version)"
test -z "$VERSION" && {
echo "Unable to get goreleaser version." >&2
exit 1
}
rm -f "$TAR_FILE"
curl -s -L -o "$TAR_FILE" \
"$RELEASES_URL/download/$VERSION/goreleaser_$(uname -s)_$(uname -m).tar.gz"
}
download
tar -xf "$TAR_FILE" -C "$TMPDIR"
"${TMPDIR}/goreleaser" "$@"
要するに GoReleaser の最新バージョンを取ってきて実行しているのである。
これで全ての準備が整ったので,コミットして origin/master にマージし,タグを討つ。 しばらくして Travis CI 側の処理が終われば Releases ページに反映される。
Changelog も GoReleaser が生成している。 コミット・ログを元に生成しているので,ログがショボいと,上の図のように, Changelog もショボくなる。 まぁ,最悪は手直しすればいいんだけどね2。
こんなところかな。
.goreleaser.yml
を自分の気に入るように調整していく作業は悩ましいが,一度出来てしまえば他プロジェクトでも使い回しがし易いと思う。そうなればリリース管理はかなり楽になるはずである。