🎉

GoReleaserで自動リリースする

2023/01/15に公開約3,900字

Go CLIのリリースを自動化したい

Goで開発したCLIツールのリリースを自動化するためにGoReleaserを利用したら便利だった。
GoのCLIリリースに関して実現したい事は以下の通り

  • クロスコンパイルしてバイナリをGitHub リリースページに掲載する
  • Homebrew向けにもリリースを行う
  • 差分をChangeLogとしてまとめる(リリースページのChangesとして掲載する)

以下では実際にec2idというツールに適用したときの手順をまとめる。

初期セットアップ

リリースの設定はQuick Startにある通り goreleaser init コマンドを実行して .goreleaser.yaml ファイルを生成する。
ただし、このファイルはリポジトリを見て動的に生成されるのではなくサンプルファイルを配置するだけなので、わざわざコマンドを実行して生成しなくてもよいかもしれない。
また、どのようなリリースを行いたいかによってこの設定ファイルをカスタマイズする必要がある。このときは、ドキュメントのカスタマイズのページが参考になる。

リポジトリに設定ファイルを配置したらCIでgoreleaserを実行するように設定する。
例えばGitHub ActionsであればGitHub Actionsのページで解説されているし、その他さまざまなCIにおけるサンプルや解説が記載されている。

以上のセットアップが完了したらGitHubでタグを打ってCIを実行する。これにより自動リリースされることが確認できる。

ビルド(クロスコンパイル)

ビルド設定を .goreleaser.yaml ファイルの builds項目に記載する。
goosgoarch などでビルド対象を指定し、 ignore に除外条件を指定する。

また、ビルドしたファイルのリリースについてはarchives項目に記載する。
GitHub Releases にてリリースするのであればビルドアーキテクチャに応じた name_temlateを指定する。その他、.debや.rpmなどのパッケージをビルドしたり、docker imageをビルドしたりできる。

Changelog

リリースページについては release項目に記載する。

特に気になったのはリリースページに記載するChangelogの記載で、こちらは独立したchangelog項目に記載する。
デフォルトのuse: gitではgit commitがすべて列挙される。コミットログ単位で整理されているのであればこれでもよいが、プルリク単位で整理している場合にはうれしくない。


(上記はuse: gitの例ですべてのコミットが列挙されてしまう)

これを解決する方法として、use: github または use: github-native を指定する。
どちらもプルリク単位で列挙してくれるのでわかりやすくなる。特にgithub-nativeを指定すれば、GitHub の automated release notes に従ってChangelogを生成してくれる。


(上記はuse: github-nativeの例でグルーピングは未適用)

homebrew対応

homebrewでもリリースしたい場合はbrews項目に記載する。
これによりhomebrewで配布するために必要なformulaeファイルを自動生成してくれる。

homebrew配布の注意点として2点あり、1つは配布用のリポジトリを別途作成する必要がある。
公式brewで配布するにはさまざまな条件があり容易ではなく、かつgoreleaserなどでは自動化できない(?)。
基本的にはTaps(Third-Party Repositories)として配布することになる。

2つ目の注意点として、homebrew-tapリポジトリにpushするための権限を設定する必要がある。
デフォルトのGITHUB_TOKENでは別のリポジトリにはpushできないので、GitHubの fine-grained personal access tokenを発行して設定する。

GitHubでfine-grained tokensを発行するときは、書き込みを許可するリポジトリとContents: Read and Writeの権限を指定する(Read access to metadataも自動的に指定される)。
また、このトークンをRepository secretsなどに登録する。

GitHub Actionsからgoreleaserを実行するときは、envで対象のトークンを指定する。

.github/workflows/release.yml
- uses: goreleaser/goreleaser-action@v4
  with:
    distribution: goreleaser
    version: latest
    args: release --rm-dist
  env:
    GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
    HOMEBREW_TAP_GITHUB_TOKEN: ${{ secrets.HOMEBREW_TAP_GITHUB_TOKEN }}

また、.goreleaser.yamlでは利用するトークンを指定する。

.goreleaser.yaml
brews:
  -
    tap:
      owner: thaim
      name: homebrew-tap
      token: "{{ .Env.HOMEBREW_TAP_GITHUB_TOKEN }}"

タグ打ちの自動化

goreleaserではタグをpushするとそれを契機にCIを実行してリリースする想定になっている。
これは便利な一方で、タグ自体は人手で管理しないといけない。v1.0.0 のタグを打ってリリースした次のリリースで、間違えて v0.5.0 のようなタグを打ってしまう可能性もある。

このようなタグの自動作成まではgoreleaserでは対応してくれない
上記のissueの通り、VERSIONファイルで管理して内容に応じて自動でタグを生成するCIを構築するといった対応が必要になる。
もしくはtagprのようなツールを利用する方法もある。これを利用すればセマンティックバージョニングに従っていれば自分で採番する必要がなくなる。

Discussion

ログインするとコメントできます