Go CLIツールのリリース完全自動化 – Release Please + GoReleaser + Homebrew

備忘録

やりたいこと

Go製のCLIツールを開発していて、以下を自動化したい。

  • バージョン番号の自動決定
  • CHANGELOG.mdの自動生成
  • GitHubリリースの自動作成
  • マルチプラットフォームバイナリのビルド
  • Homebrew Formulaの自動更新
  • Dependabot PRの自動マージ

結論として、Conventional Commitsでコミットするだけで全部自動化できた。

全体像

feat: 新機能追加
    ↓
mainにマージ
    ↓
Release Please PRが自動作成(バージョン決定 + CHANGELOG更新)
    ↓
PRをマージ(リリースのトリガー)
    ↓
GoReleaser実行
    ↓
バイナリビルド → GitHubリリース → Homebrew Formula更新

Conventional Commits

コミットメッセージの書き方ルール。Release Pleaseがこれを解析する。

feat: ダークモードを追加        # minor (0.1.0 → 0.2.0)
fix: ログインのバグを修正       # patch (0.1.0 → 0.1.1)
feat!: API形式を変更           # major (0.1.0 → 1.0.0)
docs: READMEを更新             # リリースなし
chore: CI設定を修正            # リリースなし

必要なファイル

.github/workflows/ci.yml

name: CI

on:
  push:
    branches:
      - main
      - release-please--branches--main  # Release Please PR用
  pull_request:
    branches: [main]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-go@v5
        with:
          go-version-file: go.mod
      - run: go build ./...
      - run: go test ./...

ポイント: release-please--branches--mainを追加しないと、Release Please PRでCIが走らず、ブランチ保護でマージできなくなる。

.github/workflows/release-please.yml

name: Release Please

on:
  push:
    branches: [main]

permissions:
  contents: write
  pull-requests: write

jobs:
  release-please:
    runs-on: ubuntu-latest
    outputs:
      release_created: ${{ steps.release.outputs.release_created }}
      tag_name: ${{ steps.release.outputs.tag_name }}
    steps:
      - uses: googleapis/release-please-action@v4
        id: release
        with:
          release-type: go

  goreleaser:
    needs: release-please
    if: ${{ needs.release-please.outputs.release_created }}
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
          fetch-tags: true  # タグ取得必須
      - uses: actions/setup-go@v5
        with:
          go-version-file: go.mod
      - uses: goreleaser/goreleaser-action@v6
        with:
          args: release --clean
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          HOMEBREW_TAP_GITHUB_TOKEN: ${{ secrets.HOMEBREW_TAP_GITHUB_TOKEN }}

ポイント: fetch-tags: trueを忘れるとタグ作成ステップで失敗する。

.github/workflows/auto-merge-deps.yml

Dependabot PRをCI通過後に自動マージ。

name: Auto-merge Dependabot PRs

on:
  workflow_run:
    workflows: ["CI"]
    types: [completed]

permissions:
  contents: write
  pull-requests: write

jobs:
  auto-merge:
    if: ${{ github.event.workflow_run.conclusion == 'success' }}
    runs-on: ubuntu-latest
    steps:
      - uses: actions/github-script@v7
        with:
          script: |
            const prs = context.payload.workflow_run.pull_requests;
            if (!prs?.length) return;
            const { data: pr } = await github.rest.pulls.get({
              owner: context.repo.owner,
              repo: context.repo.repo,
              pull_number: prs[0].number,
            });
            if (pr.user.login === 'dependabot[bot]') {
              await github.rest.pulls.merge({
                owner: context.repo.owner,
                repo: context.repo.repo,
                pull_number: pr.number,
                merge_method: 'squash'
              });
            }

.github/dependabot.yml

version: 2
updates:
  - package-ecosystem: gomod
    directory: /
    schedule:
      interval: weekly
  - package-ecosystem: github-actions
    directory: /
    schedule:
      interval: weekly

.goreleaser.yaml

version: 2

builds:
  - main: ./cmd/myapp
    goos:
      - linux
      - darwin
    goarch:
      - amd64
      - arm64

brews:
  - repository:
      owner: your-username
      name: homebrew-tap
      token: "{{ .Env.HOMEBREW_TAP_GITHUB_TOKEN }}"
    directory: Formula
    homepage: "https://github.com/your-username/myapp"
    description: "My awesome CLI tool"
    license: "MIT"
    install: |
      bin.install "myapp"
    test: |
      system "#{bin}/myapp", "--version"

GitHub設定

リポジトリ設定

  1. Settings → Actions → General

    • 「Allow GitHub Actions to create and approve pull requests」を有効化
  2. Settings → Secrets and variables → Actions

    • HOMEBREW_TAP_GITHUB_TOKEN: homebrew-tapリポジトリへの書き込み権限があるPersonal Access Token

homebrew-tapリポジトリ

  1. your-username/homebrew-tapリポジトリを作成
  2. ブランチ保護を設定しない(GoReleaserが直接プッシュするため)

ハマったポイント

Release Please PRでCIが走らない

GITHUB_TOKENで作成されたPRはpull_requestイベントをトリガーしない仕様。
release-please--branches--mainをpushトリガーに追加して解決。

タグ作成ステップが失敗する

actions/checkoutはデフォルトでタグを取得しない。
fetch-tags: trueを追加。

homebrew-tapへのプッシュが403/409

homebrew-tapにブランチ保護(PR必須)が設定されていた。
→ ブランチ保護を解除。

CHANGELOGの自動生成

Release Pleaseが自動でCHANGELOG.mdを更新する。

## [0.2.0](https://github.com/user/repo/compare/v0.1.0...v0.2.0) (2026-01-20)

### Features

* ダークモードを追加 ([#12](https://github.com/user/repo/issues/12))

### Bug Fixes

* ログインエラーを修正 ([#10](https://github.com/user/repo/issues/10))

GitHubリリースページにも同じ内容が自動で記載される。

日常の開発フロー

# 機能開発
git checkout -b feature/dark-mode
# ... 実装 ...
git commit -m "feat: ダークモードを追加"
git push origin feature/dark-mode
# PR作成 → マージ

# → Release Please PRが自動作成される
# → 区切りの良いところでRelease Please PRをマージ
# → 自動でリリース & Homebrew更新

これで、コードを書くことだけに集中できる環境が整った。

タイトルとURLをコピーしました