2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

goreleaser で自作ツールを簡単に Homebrew 対応

2
Posted at

Go言語で書いたツールをビルドして配布するのに GoReleaser を使って GitHub の releases 機能を使っていました。
Go言語で書けばシングルバイナリで配布もファイルをそのままダウンロードできるようにしおくことで、利用時の手間がすくなくて便利と思ってこれまで tar.gz や zip にしないで実行用バイナリをそのままダウンロードできるようにしてました。

しかし、なにがきっかけだったか GoReleaser で Homebrew 対応が簡単にできることを知りました。

GitHub Actions で GoReleaser を実行して Homebrew Tap を更新する構成

Homebrew の Tap 用 GitHub プロジェクト名は homebrew- で始めなければなりません。そして、それに続く文字列が brew tap myorg/mytapmytap 部分になります。ひとつの 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 を入力
  • WebhookActive にあるチェックボックスのチェックを外す
  • Permissions
    • Repository permissions
      • Contents: Read and write
      • Metadata: Read-only (デフォルトで有効になっており外すことはできない)
  • Where can this GitHub App be installed?
    • Only on this account (自分しか使わない)

作成したら Generate a private key リンクで private key を生成しダウンロードする。App ID をメモする。
この Private keyApp 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_IDAPP_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
2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?