Go言語で書いたツールをビルドして配布するのに GoReleaser を使って GitHub の releases 機能を使っていました。
Go言語で書けばシングルバイナリで配布もファイルをそのままダウンロードできるようにしおくことで、利用時の手間がすくなくて便利と思ってこれまで tar.gz や zip にしないで実行用バイナリをそのままダウンロードできるようにしてました。
しかし、なにがきっかけだったか GoReleaser で Homebrew 対応が簡単にできることを知りました。
GitHub Actions で GoReleaser を実行して Homebrew Tap を更新する構成
Homebrew の Tap 用 GitHub プロジェクト名は homebrew- で始めなければなりません。そして、それに続く文字列が brew tap myorg/mytap の mytap 部分になります。ひとつの Tap 用プロジェクトで複数のツールを管理することが可能です。
GitHub App の作成
GoReleaser はそのツール自身の build と release だけでなく、Homebrew 用のTap リポジトリのファイルも更新する必要があるため、通常の GITHUB_TOKEN では権限が足りず、GitHub App を作成し複数プロジェクトにまたがったアクセス権が取得できるようにします。
GitHub App を作成するには Developer Settings から [New GitHub App] ボタンで作成フォームに進みます。
- GitHub App name にはグローバルでユニークな任意の値を入力
- Homepage URL にはとりあえず homebrew 用プロジェクトの URL を入力
- Webhook の Active にあるチェックボックスのチェックを外す
-
Permissions
- Repository permissions
- Contents: Read and write
- Metadata: Read-only (デフォルトで有効になっており外すことはできない)
- Repository permissions
-
Where can this GitHub App be installed?
- Only on this account (自分しか使わない)
作成したら Generate a private key リンクで private key を生成しダウンロードする。App ID をメモする。
この Private key と App ID は GitHub Actions の中で使うため、ツールの Secrets に登録する。
GoReleaser の設定
# This is an example .goreleaser.yml file with some sensible defaults.
# Make sure to check the documentation at https://goreleaser.com
# The lines below are called `modelines`. See `:help modeline`
# Feel free to remove those if you don't want/need to use them.
# yaml-language-server: $schema=https://goreleaser.com/static/schema.json
# vim: set ts=2 sw=2 tw=0 fo=cnqoj
version: 2
before:
hooks:
# You may remove this if you don't use go modules.
- go mod tidy
# you may remove this if you don't need go generate
- go generate ./...
builds:
- env:
- CGO_ENABLED=0
goos:
- linux
- windows
- darwin
goarch:
- amd64
- arm64
ldflags:
- -s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}}
archives:
- name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}"
files:
- LICENSE
- README.md
checksum:
name_template: 'checksums.txt'
changelog:
sort: asc
filters:
exclude:
- "^docs:"
- "^test:"
- '^[mM]erge'
homebrew_casks:
- repository:
owner: myorg # 環境に合わせて書き換える
name: homebrew-tools
token: "{{ .Env.GITHUB_TOKEN }}"
pull_request:
enabled: false
hooks:
post:
install: |
if OS.mac?
system_command "/usr/bin/xattr", args: ["-dr", "com.apple.quarantine", "#{staged_path}/{{ .ProjectName }}"]
end
homebrew_casks の設定が今回のキモです。
この例では pull_request を無効にしていますが、同時にいくつものツールの更新が行われる場合 conflict が発生してコケる可能性があるため pull_request を有効にした方が安全です。毎回手動で merge するのも面倒なので auto merge するような仕組みも入れた方が良いかもしれません。
macOS では外部からダウンロードされた実行ファイルが署名及び xxx されていない場合、次のようなメッセージが表示されてシステム設定のプライバシーとセキュリティから実行を許可する必要があります。
自分用のツールですし、毎度許可処理を行うのが面倒なので hooks 設定で許可を自動化しています。
Apple は、"xxx" に Mac に損害を与えたり、プライバシーを侵害する可能性のあるマルウェアが含まれていないことを検証できませんでした。
Apple Developer Program に入っていれば署名と公証も GoReleaser で行うことができるようです。
GitHub Actions の設定
ツールの GitHub プロジェクトで Settings → Secrets and variables → Actions から Repository secrets に GitHub App 作成時にメモした App ID とダウンロードした Private key を登録します。
ここでは名前をそれぞれ APP_ID、APP_PRIVATE_KEY としています。
name: GoReleaser
on:
pull_request:
branches:
- main
push:
tags:
- 'v*'
jobs:
goreleaser:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Extract Go version
id: go-version
run: echo "version=$(grep 'go = ' mise.toml | cut -d '"' -f 2)" >> $GITHUB_OUTPUT
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: ${{ steps.go-version.outputs.version }}
cache: true
- name: Run GoReleaser build check (Pull Request)
if: github.event_name == 'pull_request'
uses: goreleaser/goreleaser-action@v6
with:
version: v2
args: build --snapshot --clean
# GitHub App から一時的なトークンを生成
- name: Generate Token
if: startsWith(github.ref, 'refs/tags/')
id: app-token
uses: actions/create-github-app-token@v1
with:
app-id: ${{ secrets.APP_ID }}
private-key: ${{ secrets.APP_PRIVATE_KEY }}
owner: ${{ github.repository_owner }}
# 自分のリポジトリと Tap リポジトリの両方に権限を絞る(任意)
repositories: |
mytool
homebrew-tools
- name: Run GoReleaser release (Tag Push)
if: startsWith(github.ref, 'refs/tags/')
uses: goreleaser/goreleaser-action@v6
with:
version: v2
args: release --clean
env:
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
Homebrew でツールをインストールする
Homebrew 用の Tap リポジトリに Cask のファイルが追加されたら brew tap myorg/tools などとして tap を追加することで自分用ツールが簡単に手元にインストールできるようになります。
brew tap myorg/tools
brew install --cask mytool