My External Storage

Mar 9, 2025 - 6 minute read - Comments - github go

tagprとGoReleaserを使ったOSSの自動リリース

去年、3年ぶりくらいに自作のCLIツールをリリースしたけど、どうやってリリースしてたかすっかり忘れていた。
CIは壊れてたし、せっかくなのでtagprGoReleaser(とGitHub App)を使って2024年らしいデプロイパイプラインを作り直した。
リリースPRをマージしたらHomebrewへのバイナリリリースまで自動化できて満足。ちょっと時間が経ってしまったけどメモを残しておく。

TL;DR

なお、この記事では自作ツールをHomebrew-tap対応する方法は触れない。

また、このあと説明で記載するYAMLは次のリポジトリで使われることを想定している。

久しぶりにOSSをメンテすると何も覚えていない

去年、3年ほど放置していた自作OSSのCLIツールを久々にいくつかメンテした。 ツールのコードのメンテ自体はすぐできたけど、リリース手順を完全に忘れていた。
放置前からGitHub Actionsを使ったバイナリリリースの設定などはしていたものの、GoReleaserのActionsをアップデートしたら壊れてしまったりしたので、令和のイケてるリリースフローを作るためにしっかり作り直すことにした。

自動化の方針

最終的に実現した流れは以下の通り。

  • メインブランチにPRをマージすると、リリース用のPRが作成される
    • 次の修正PRをマージすると自動的にリリース用のPRが更新される
  • メインブランチにリリース用のPRをマージすると、GitHub上でリリースタグが作成される
    • CHANGELOGも自動生成する
  • リリースされたらGoReleasrがバイナリデータを作成し、GitHubとHomebrew-tap用のリポジトリにバイナリをアップロードする。
    • 具体的には、CLIツールのリポジトリへのバイナリアップロードとhomebrew-tap用のリポジトリを更新する

やったこと

具体的には次の設定を行った。

  • GitHub Appを使った認証トークンの発行準備
  • tagprの設定
  • tagprGoReleaserを組み合わせたActionsフローの作成

GitHub Appで認証情報を管理

Actions内で複数のリポジトリを操作するときは認証トークンの発行が必要になる。 GitHub Appを使えばトークンの有効期限を心配する必要もないようなので、こちらを利用することにした。

GitHub Appを作成しておけば、この公式actionでActions内でトークンを利用できる。

具体的な手順は公式の手順書をみればできる。

今回実現するフローだと、以下のリソースに対してRead/Write権限が必要になる

  • Code
    • これはデフォルト有効かも
  • Pull Requets
  • Contents
  • Workflows

これを個人アカウントにインストールしておく。 あとは該当リポジトリのシークレットに秘密鍵を登録してActionsにcreate-github-app-tokenのステップを追加する。 後続のステップではsteps.app-token.outputs.tokenを参照することで認証が必要なGitHubの操作が可能になる。

jobs:
  first-job-name:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/create-github-app-token@v1
        id: app-token
        with:
          app-id: ${{ secrets.APP_ID }}
          private-key: ${{ secrets.PRIVATE_KEY }}
      - name: Check out source code
        uses: actions/checkout@v4
        with:
          token: ${{ steps.app-token.outputs.token }}

ハマったところ

GitHub App自体のPermissionを調整した後は、個人アカウント側で更新内容を受け入れる必要がある。

https://github.com/settings/installations

2. tagprを使ったリリースPR作成の自動化

tagprは、バージョンタグの作成やリリース用のPRを自動で生成してくれるツールだ。

  • Pushイベントをトリガーに、次のリリースタグの自動判定・PR作成を実施
    • ラベルを使うことでパッチバージョンアップ、メジャーバージョアップなどが可能
  • リリース用PRマージすることで自動的に新しいリリースタグが設定される

リポジトリのREADMEを読みながら設定すれば簡単にセットアップできた。ハマったところもなし。 CHANGELOGの自動生成もしてくれるし、めちゃくちゃ便利。

.tagpr、リポジトリに追加した後は次のようなActionsを作成すればよい。

name: tag-and-release

on:
  push:
    branches:
      - master
permissions:
  contents: write
  pull-requests: write

jobs:
  tagpr:
    runs-on: ubuntu-latest
    outputs:
      tagpr-tag: ${{ steps.run-tagpr.outputs.tag }}
    steps:
      - uses: actions/create-github-app-token@v1
        id: app-token
        with:
          app-id: ${{ secrets.APP_ID }}
          private-key: ${{ secrets.PRIVATE_KEY }}
      - name: Check out source code
        uses: actions/checkout@v4
        with:
          token: ${{ steps.app-token.outputs.token }}
      - id: run-tagpr
        name: Run tagpr
        uses: Songmu/tagpr@v1
        env:
          GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}

バイナリを作成する必要がないライブラリ系のリポジトリならばここで終わりでもよい。

3. GoReleaserで自動リリース

GoReleaserはGo(Rustもできるらしい)で書かれたソフトウェアのリリースプロセスを自動化してくれるツール。

  • クロスコンパイル対応もしてくれる
  • Homebrewタップへの反映なども自動化できる

こちらは.goreleaser.ymlを作成したあとは、以下のようなjobを定義すれば、GitHub Appで取得した認証情報を使ってバイナリをリリースできる。

  goreleaser:
    needs: tagpr
    if: needs.tagpr.outputs.tagpr-tag != ''
    runs-on: ubuntu-latest
    steps:
      - uses: actions/create-github-app-token@v1
        id: app-token
        with:
          app-id: ${{ secrets.APP_ID }}
          private-key: ${{ secrets.PRIVATE_KEY }}
          owner: "budougumi0617"
          repositories: |
            nrseg
            homebrew-tap
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
          token: ${{ steps.app-token.outputs.token }}
      - name: Set up Go
        uses: actions/setup-go@v5
        with:
          go-version-file: go.mod
          cache: true
      - name: Run GoReleaser
        uses: goreleaser/goreleaser-action@v6
        with:
          distribution: goreleaser
          version: "latest"
          args: release --clean
        env:
          GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}

年のため、GitHub Appに作業できるリポジトリを明示的に指定している。

          repositories: |
            nrseg
            homebrew-tap

ハマったポイント

今回メンテしたリポジトリでリリースしたバイナリを使ったActionsを書いていたので、リリースされるファイルパスなどの調整でちょっとハマった。 まっさらな状態からリリースするならばそんなにハマらないと思う。

最終的なGitHub ActionsとActionsで作成されるもの

ここまでの設定をすべてまとめると、次のようなActionsになる。

https://github.com/budougumi0617/nrseg/blob/v0.0.10/.goreleaser.yml

name: tag-and-release

on:
  push:
    branches:
      - master
permissions:
  contents: write
  pull-requests: write

jobs:
  tagpr:
    runs-on: ubuntu-latest
    outputs:
      tagpr-tag: ${{ steps.run-tagpr.outputs.tag }}
    steps:
      - uses: actions/create-github-app-token@v1
        id: app-token
        with:
          app-id: ${{ secrets.APP_ID }}
          private-key: ${{ secrets.PRIVATE_KEY }}
      - name: Check out source code
        uses: actions/checkout@v4
        with:
          token: ${{ steps.app-token.outputs.token }}
      - id: run-tagpr
        name: Run tagpr
        uses: Songmu/tagpr@v1
        env:
          GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
  goreleaser:
    needs: tagpr
    if: needs.tagpr.outputs.tagpr-tag != ''
    runs-on: ubuntu-latest
    steps:
      - uses: actions/create-github-app-token@v1
        id: app-token
        with:
          app-id: ${{ secrets.APP_ID }}
          private-key: ${{ secrets.PRIVATE_KEY }}
          owner: "budougumi0617"
          repositories: |
            nrseg
            homebrew-tap
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
          token: ${{ steps.app-token.outputs.token }}
      - name: Set up Go
        uses: actions/setup-go@v5
        with:
          go-version-file: go.mod
          cache: true
      - name: Run GoReleaser
        uses: goreleaser/goreleaser-action@v6
        with:
          # either 'goreleaser' (default) or 'goreleaser-pro'
          distribution: goreleaser
          # 'latest', 'nightly', or a semver
          version: "latest"
          args: release --clean
        env:
          # need to access other repository for brew-tap
          GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}

このActionsを設定した後、リポジトリで修正PRをマージすると、次のようなリリース用PRが作成される。

このPRをマージすると、リリースタグが作成され、バイナリも配置される

CHANGELOGも自動生成される。うれしい。

おわりに

GitHub Actionsを活用することで、リリース作業をほぼ完全に自動化できた。 自分のOSSを作るときはこれをテンプレにしたい(最近は作れていないけれど…)。
数年放置していたけれど、go.modが入った後だったしGoのコードは問題なく動くのも確認できた。よい。
久しぶりにちゃんとActionsを自分で書いたら認証トークンの払い出し方とか変わっていて、(PAT払い出してシークレットに設定して終わりだった)ちょっと前は放牧的な時代だったんだなーと思った。

参考

関連記事

Tags: github github actions tagpr goreleaser

2024年の振り返り

comments powered by Disqus