cgoを利用した Go project を GitHub Actions でクロスコンパイル&リリースを行う
check-lastlogというmackerel監視pluginを作ってるのですが、
こちらは /var/log/lastlog を読むために一部cgoを使っています。mackerelのpluginなので一つ前の記事で紹介した方法で
リリースしたいところなのですが、GoReleaserでは、cgoへの依存があるプロジェクトのクロスコンパイルがサポートされていないため、ひと工夫必要になります。
check-lastlogではこちらのxgoを使う方法でクロスコンパイルし、GoReleaserでGitHub Releaseにアップロードをしています。
出来上がったGitHub Actionsのワークフローはこちら
https://github.com/kazeburo/check-lastlog/blob/master/.github/workflows/release.yml
xgoでのクロスコンパイル
xgo はGoのクロスコンパイルの環境がはいったDockerのイメージと各環境をターゲットとしたビルドを行うコマンドを提供しています。
check-lastlogではGitHub Actionが用意されているこちら
を使いました。こちらはforkしたxgoを使っているようです。
check-lastlogのワークフロー、.github/workflows/release.yml の前半ではxgoを使ってクロスコンパイルしています。
name: release
on:
push:
tags:
- "v[0-9]+.[0-9]+.[0-9]+"
jobs:
goreleaser:
runs-on: ubuntu-latest
env:
BIN_NAME: check-lastlog
steps:
- name: Checkout
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Set tag to environment variable
id: set-tag
run: echo ::set-output name=version::${GITHUB_REF#refs/*/}
- name: Build with xgo
uses: crazy-max/ghaction-xgo@v1
with:
xgo_version: latest
go_version: 1.15.x
dest: build
prefix: ${{ env.BIN_NAME }}
targets: linux/amd64,linux/arm64
v: true
x: false
ldflags: -s -w -X main.version=${{ steps.set-tag.outputs.version }}
- name: Check build dir
run: ls -lR build
ghaction-xgo のパラメータは次のようになってます。
- dest: ビルドしたバイナリを格納するディレクトリ
- prefix: バイナリのファイル名。後ろに
-${OS}-${Arch}がつきます - targets: クロスコンパイルするターゲット。カンマで区切る
- v: ビルドしたファイル名の表示
- x: ビルドの際のコマンドなどの表示
また、GoReleaser ではldflagsをデフォルトでいくつか設定してくれますが、xgoはそうではないので、-w -s と -X main.version を追加しています。
これでビルドが完了すると、dest で指定したディレクトリにバイナリができます。
> Run ls -lR build build: total 3428 -rwxr-xr-x 1 runner docker 1806024 Dec 2 09:12 check-lastlog-linux-amd64 -rwxr-xr-x 1 runner docker 1703448 Dec 2 09:12 check-lastlog-linux-arm64
アーカイブ作成とGitHub Releaseへのアップロード
あとは、GitHub Releaseへのアップロードだけではあるのですが、check-lastlog はmackerel監視プラグインなので、mkr plugin install 対応をさせます。
mkr plugin install 対応するためにはファイル名が {{ .ProjectName }}_{{ .Os }}_{{ .Arch }}.zip でzipでアーカイブする必要があります。なのでShellで頑張ってzipをつくりました。
- name: Zip binaries
run: |
cd build
for file in ./* ; do
mkdir $(echo ${file}|awk -F- '{print "${{ env.BIN_NAME }}_"$(NF-1)"_"$(NF)}') &&
cp ${file} $(echo ${file}|awk -F- '{print "${{ env.BIN_NAME }}_"$(NF-1)"_"$(NF)}')/${{ env.BIN_NAME }} &&
zip $(echo ${file}|awk -F- '{print "${{ env.BIN_NAME }}_"$(NF-1)"_"$(NF)}').zip -j $(echo ${file}|awk -F- '{print "${{ env.BIN_NAME }}_"$(NF-1)"_"$(NF)}')/${{ env.BIN_NAME }} ../README.md ../LICENSE;
done
shasum -a 256 *.zip > ${{ env.BIN_NAME }}_${{ steps.set-tag.outputs.version }}_checksums.txt
ls -lR ./
バイナリだけではなく、README.mdやLICENSEファイルもアーカイブに同梱しています。また、checksum用のファイルも作っています。GoReleaserのchecksumがsha256なので、同じようにshasumコマンドを使ってみました。
そして最後にGitHub Releaseにアップロードするのですが、CHANGESも作ってくれるGoReleaserを使います。
まず、GoReleaserの設定ファイル .goreleaser.yml
# Use for generating CHANGELOG
builds:
- binary: check-lastlog
skip: true
release:
github:
owner: kazeburo
name: check-lastlog
extra_files:
- glob: "build/*.zip"
- glob: "build/*_checksums.txt"
ビルド(builds)は skip:true で止めることができました。成果物がない場合、archiveの作成も行われないようでした。GoReleaserで作ったパッケージのかわりに、xgoとShellで作ったzipとchecksumファイルをextra_filesに指定してアップロードしてもらいます。
GitHub Actionsのワークフローは goreleaser/goreleaser-action を使いました。
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v2
with:
version: latest
args: release --rm-dist
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GoReleaser 便利
まとめ
xgoを使うとGoReleaserのみでクロスコンパイルする場合にくらべて実行時間は伸びてしまいますが、cgoを使っているGo Projectでもlinuxのamd64, arm64に対応したパッケージを作ることができました。