🐈

GoreleaserとGitHub ActionsでプライベートリポジトリのCLIツールをbrewに公開する

2024/02/20に公開

はじめに

自作したツールのソースコードを公開しないけどパッケージツールbrewに公開して多くの人に使ってもらえるようにしたい!

こういうめんどくさい願いも技術的には叶えることができるらしい?ということでトライをし続けていました。相当に格闘したのでその記録を残しておこうと思います。プライベートリポジトリのツールのバイナリ部分だけを公開するというのはニッチな需要かもしれません。しかし、世の中にはたくさんのプライベートツールが開発されていて、その大部分は限定合理的な場で使われており、公開されていません。しかし、もし簡単にそれらのCLIインターフェース部分を切り出して(もしくは自作して)公開することができるのであれば、バイナリ部分だけを公開してもいいとされるツールは法人をはじめとする世の中にたくさん眠っているのではないでしょうか?そういったツールがこの記事を見たことをきっかけに世の中に出てくることにつながればと思います。

brewなどのパッケージツールから使えるようになれば、開発者、利用者双方に良いことはたくさんあります。CLIツールなのにわざわざブラウザ行ってリリースページから落としてきてパスを通して...といった作業はコンテキストスイッチが多すぎるため、やりたくありません。また、新しいバージョンをリリースした際のupdateもbrew側から自動でできますので再インストールの必要性も全くなく非常に良いです。

元ネタ紹介

筆者は今年のSecHack365事業で以下のようなものを作っていて元ネタはそこからです。
GitHub Actionsのworkflowsのyamlを対象に
・yamlによるworkflowsの構築支援としてboilerplateを吐き出す仕組み
・yamlの字句・構文に対する静的で高速かつ詳細なデバッグ支援
・問題発見時のドキュメントへの照合(エラー文中)
通常の静的解析では拾いにくいセキュリティーや運用などの意味レベルのルールをSASTとして組み込むことで「字句・構文」などのエラーと一緒に出力する

などができるCLIアプリ、sisakulintを開発しています。ソースコードの9割がオープンソースではないですが、開発したすべての成果物が利用できる仕組みを作っており今回はその詳細な仕組みの紹介となります。制作物に関しては、次回のブログで詳細な内容を書いて公開しますのでお待ちください。

https://github.com/ultra-supara/homebrew-sisakulint

Goreleaser + GitHub Actions

GoにはgoreleaserというGo製ツールのリリースプロセスを自動化するためのエコシステムが整備されています。GitHub・GitLab・Giteaへのリリースの他、Linuxパッケージや Homebrew Tapsの作成など、Goプロジェクトのリリースに関する様々なタスクを自動化することができます。

それをGitHub Actionsと連携してリリースするということに関して「パブリックリポジトリのCLIツール」であれば概ねこちらのブログで事足りると思います。

https://zenn.dev/kou_pg_0131/articles/goreleaser-usage

よってこちらの流れを踏襲しつつプライベートリポジトリのツールのリリースの場合はどのような差分があるのかを解説します。

プライベートリポジトリの場合

図解するとこんな感じになります。

結論から逆算するのがわかりやすいと思うのでそれぞれのファイルの詳細を見ていくことにします。

プライベートリポジトリのrelease.yaml

GitHub ActionsとGoReleaserを使用して、Go製のCLIツールをリリースする過程を把握したところで実際の中身に入っていきます。このプロセスは、ultra-supara/sisakulintというプライベートリポジトリの.github/workflows/release.yamlファイルによって定義されています。このファイルは、特定のタグがプッシュされた時にトリガーされるリリースの自動化を設定しています。

ultra-supara/sisakulint/.github/workflows/release.yaml(🔒プライベートリポジトリ)

name: Release

on:
  push:
    tags:
      - "v*.*.*"

permissions:
  contents: write

jobs:
  goreleaser:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Set up Go
        uses: actions/setup-go@v4
        with:
          go-version: "1.21.4"

      - name: Run GoReleaser
        uses: goreleaser/goreleaser-action@v4
        with:
          version: latest
          args: release --rm-dist
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          TAP_GITHUB_TOKEN: ${{ secrets.TAP_GITHUB_TOKEN }}

  after_release:
    needs: goreleaser
    runs-on: ubuntu-latest
    steps:
      - name: Checkout tap repository
        run: |
          git clone https://github.com/ultra-supara/homebrew-sisakulint
          git config --global url."https://x-oauth-basic:${{ secrets.TAP_ACCESS_TOKEN }}@github.com".insteadOf "https://github.com"
      - name: Update formula
        run: |
          sed -i -e 's/# typed:/require_relative "strategy" # typed:/g' homebrew-sisakulint/sisakulint.rb
          cat homebrew-sisakulint/sisakulint.rb
      - name: Commit and push changes
        run: |
          cd homebrew-sisakulint
          git config --global user.name "sisakulint-bot"
          git config --global user.email "actions@github.com"
          git add sisakulint.rb
          git commit -m "Update sisakulint.rb"
          git push origin HEAD:main
          git tag ${{ github.ref_name }}
          git push origin ${{ github.ref_name }}

リリースプロセスの自動化

GitHub Actionsを利用したこの自動化プロセスは、v*.*.*形式のタグがリポジトリにプッシュされた際に開始します。このアプローチにより、バージョン管理が明確になり、リリース作業の自動化が可能になります。

コードのチェックアウト

まず、GitHub Actionsのactions/checkout@v4を使用してリポジトリのコードをチェックアウトします。fetch-depth: 0の設定により、リポジトリの全

履歴を取得します。これは、後続のステップでバージョン情報などを正確に把握するために重要です。

Goのセットアップ

次に、actions/setup-go@v4を使用してGo言語の環境をセットアップします。ここでは、Goのバージョン1.21.4を指定しています。これにより、ビルド環境が一貫しており、開発時と同じバージョンでのコンパイルとテストが保証されます。

GoReleaserの実行

GoReleaserは、Go製のアプリケーションをビルドし、リリースするためのツールです。goreleaser/goreleaser-action@v4を使用してGoReleaserを実行し、引数にrelease --rm-distを渡すことで、以前のリリース成果物を削除した上で新しいリリースを作成します。このステップでは、GITHUB_TOKENTAP_GITHUB_TOKENというシークレットを環境変数として使用します。これらのトークンは、GitHubとの認証に必要です。

Tapリポジトリのチェックアウトと更新

リリース後の作業として、HomebrewのTapリポジトリを更新する手順があります。まず、git cloneコマンドでultra-supara/homebrew-sisakulintリポジトリをチェックアウトします。その後、認証のためにGitのURL書き換え設定を行い、TAP_ACCESS_TOKENシークレットを使用します。

フォーミュラファイルsisakulint.rbの更新では、特定の文字列を置換することでファイルを必要な形に調整し、その変更をコミットしてメインブランチにプッシュします。このプロセスは、自動化されたボットアカウントsisakulint-botによって実行され、actions@github.comをメールアドレスに使用します。

この一連のステップを通じて、Go製CLIツールのビルド、リリース、そしてHomebrewフォーミュラの更新が完全に自動化されます。これにより、開発者はリリースプロセスの手間を大幅に削減し、より重要な開発作業に集中できるようになります。GitHub ActionsとGoReleaserを組み合わせることで、バージョン管理されたタグがリポジトリにプッシュされると、自動的にビルドとリリースのプロセスが開始されます。

最後のステップでは、リリースが成功した後に、関連するHomebrew Tapリポジトリも自動的に更新されます。これは、ソフトウェアの配布をさらに自動化し、エンドユーザーが最新バージョンを容易にインストールできるようにするための重要なステップです。git tagで新しいバージョンのタグを作成し、git pushでリモートのリポジトリにプッシュしてユーザーは常に最新のリリースを利用できるようになります。

この自動化されたリリースプロセスは、開発効率の向上だけでなく、ソフトウェアの品質保持にも寄与します。自動テスト、ビルド、リリースを組み合わせることで、手動のエラーを減らし、一貫したリリース品質を確保できます。また、このプロセスは新しい機能や修正を迅速にリリースする能力が高いです。

.goreleaser.yaml

次にgoreleaserの設定ファイルです。

ultra-supara/sisakulint/.goreleaser.yaml(🔒プライベートリポジトリ)

release:
  draft: false

before:
  hooks:
    - go mod download

builds:
  - <<: &build_defaults
      main: ./cmd/sisakulint
      ldflags: -s -w -X github.com/ultra-supara/sisakulint.version={{.Version}} -X "github.com/ultra-supara/sisakulint.installedFrom=installed from {{.Tag}}"
      env:
        - CGO_ENABLED=0
    id: macos
    goos: [darwin]
    goarch: [amd64, arm64]

  - <<: *build_defaults
    id: linux
    goos: [linux]
    goarch: [arm, amd64, arm64]

brews:
  - name: sisakulint
    url_template: "https://github.com/ultra-supara/homebrew-sisakulint/releases/download/{{ .Tag }}/{{ .ArtifactName }}"
    commit_author:
      name: "github-actions[bot]"
      email: "41898282+github-actions[bot]@users.noreply.github.com"
    homepage: https://github.com/ultra-supara/sisakulint
    description: Support tools for GitHub Actions workflow files
    download_strategy: GitHubPrivateRepositoryReleaseDownloadStrategy
    install: |
      bin.install "sisakulint"
    test: |
      system "#{bin}/sisakulint -version"
    repository:
      owner: ultra-supara # Homebrew Taps 用のリポジトリのオーナー名
      name: homebrew-sisakulint # Homebrew Taps 用のリポジトリ名
      token: "{{ .Env.TAP_GITHUB_TOKEN }}" # `TAP_GITHUB_TOKEN` 環境変数をトークンとして使うようにする

changelog:
  skip: true

GoReleaserを用いたsisakulintのリリースプロセスの自動化は、その設定ファイル.goreleaser.yamlによって細かく定義されています。このファイルは、ビルド、リリース、そしてHomebrew Formulaの生成を含む一連のプロセスを指定しており、開発者が手動で行う作業を最小限に抑えることを目的としています。以下では、この設定ファイルの主要なセクションを解説し、それがどのように機能するかを詳しく見ていきます。

リリースの設定

リリースセクションでは、ドラフトリリースを作成しないように設定しています。これにより、タグがプッシュされると直接公開リリースが作成されるため、リリースプロセスが迅速化されます。

ビルド前のフック

ビルドを行う前に、Goの依存関係をダウンロードするためのフックが設定されています。これはビルドプロセスの一貫性を保証し、全ての依存関係が最新の状態であることを確認するために必要です。

ビルドの定義

ビルドセクションでは、MacOSとLinux向けのビルド設定が指定されており、各プラットフォームに対して異なるアーキテクチャが設定されています。ビルドには標準的なフラグが使用され、ビルド時にバージョン情報やインストール方法などのメタデータがバイナリに埋め込まれます。これにより、バイナリ自体が自己文書化され、どのバージョンがどのようにしてインストールされたかをユーザーが簡単に確認できるようになります。

Homebrew Formulaの生成

GoReleaserは、自動的にHomebrew Formulaを生成します。このセクションでは、Formulaの名前、説明、ホームページURL、ダウンロード戦略、インストールスクリプト、テストコマンドなどが定義されています。また、Homebrew Taps用のリポジトリ情報として、オーナー名とリポジトリ名が指定され、環境変数から取得したトークンを使用してGitHubへのアクセスが設定されています。

change logのスキップ

chenge logの生成をスキップする設定により、リリースプロセスがさらにスリム化されています。この設定は、チームが他の方法でリリースノートを管理している場合とかに便利だと思います。
.goreleaser.yamlの設定ファイルを通じて、リリースのたびに一貫した方法でビルドし、配布するプロセスを確立しています。

連携の仕組み

.goreleaser.yaml.github/workflows/release.yamlファイルは、ソフトウェアの自動リリースプロセスにおいて連携して機能します。これらのファイルはそれぞれ異なる役割を担いつつ、共にソフトウェア開発の自動化と効率化を目指すものです。以下にその関連性について解説します。

.github/workflows/release.yamlワークフローによってGoReleaserが実行されると、goreleaserコマンドは.goreleaser.yamlファイルの設定に従って動作します。つまり、GitHub Actionsはリリースプロセスをトリガーするための「起動装置」として機能し、GoReleaserはそのトリガーに応じて具体的なビルドとリリースの作業を行う「実行エンジン」として機能します。

このように、.github/workflows/release.yamlはGoReleaserを起動するための条件と環境を設定し、.goreleaser.yamlはGoReleaserがどのようにビルドとリリースを実行するかを具体的に定義します。これらのファイルが連携することで、開発者はソフトウェアのリリースプロセスを完全に自動化でき、手作業によるエラーを削減し、リリースの効率と一貫性を大幅に向上させることができます。

sisakulint.rb

リリースプロセスの自動化に続き、私たちは特定のツールの配布方法にも目を向ける必要があります。内部でsisakulint.rbが必要とされているのでそのファイルについても解説を加えます。

ultra-supara/homebrew-sisakulint/sisakulint.rb(パブリックリポジトリ)

require_relative "strategy" # typed: false
# frozen_string_literal: true

# This file was generated by GoReleaser. DO NOT EDIT.
class Sisakulint < Formula
  desc "Support tools for GitHub Actions workflow files"
  homepage "https://github.com/ultra-supara/sisakulint"
  version "0.0.3"

  on_macos do
    if Hardware::CPU.arm?
      url "https://github.com/ultra-supara/homebrew-sisakulint/releases/download/v0.0.3/sisakulint_0.0.3_darwin_arm64.tar.gz", using: GitHubPrivateRepositoryReleaseDownloadStrategy
      sha256 "5d1e8f3907e1008adea6583f42669096ce6099e8764ffb0bcd4aef0685794874"

      def install
        bin.install "sisakulint"
      end
    end
    if Hardware::CPU.intel?
      url "https://github.com/ultra-supara/homebrew-sisakulint/releases/download/v0.0.3/sisakulint_0.0.3_darwin_amd64.tar.gz", using: GitHubPrivateRepositoryReleaseDownloadStrategy
      sha256 "9fab8187c7af9ab8ae2bb0f8e3c24aa64821e63ce32148e16ceda5d6ff1471cf"

      def install
        bin.install "sisakulint"
      end
    end
  end

  on_linux do
    if Hardware::CPU.arm? && Hardware::CPU.is_64_bit?
      url "https://github.com/ultra-supara/homebrew-sisakulint/releases/download/v0.0.3/sisakulint_0.0.3_linux_arm64.tar.gz", using: GitHubPrivateRepositoryReleaseDownloadStrategy
      sha256 "4f7a0b799542befd3445fb1c0254b9c9cff6eea6c953ea787b66cb577ac54c52"

      def install
        bin.install "sisakulint"
      end
    end
    if Hardware::CPU.intel?
      url "https://github.com/ultra-supara/homebrew-sisakulint/releases/download/v0.0.3/sisakulint_0.0.3_linux_amd64.tar.gz", using: GitHubPrivateRepositoryReleaseDownloadStrategy
      sha256 "778f0f4eeff3fde53e0cc513b1c23992239e6f8978d0c1418939e3ecf1f57f06"

      def install
        bin.install "sisakulint"
      end
    end
    if Hardware::CPU.arm? && !Hardware::CPU.is_64_bit?
      url "https://github.com/ultra-supara/homebrew-sisakulint/releases/download/v0.0.3/sisakulint_0.0.3_linux_armv6.tar.gz", using: GitHubPrivateRepositoryReleaseDownloadStrategy
      sha256 "c9c26c0d63c7ce6b54fab01e935a4b87deefa62e23fc4ef450a62fa7264897d5"

      def install
        bin.install "sisakulint"
      end
    end
  end

  test do
    system "#{bin}/sisakulint -version"
  end
end

ここでの焦点は、ultra-supara/homebrew-sisakulintリポジトリ内にあるsisakulint.rb、すなわちHomebrew Formulaのファイルに置かれます。このファイルは、Go製のCLIツールをMacOSおよびLinuxユーザーが簡単にインストールできるようにするためのものです。ここでは、このファイルがどのように構成されているか、そしてそれがどのようにしてユーザーのインストール体験を向上させるかを解説します。

Homebrew Formulaの役割

sisakulint.rbは、GoReleaserによって自動生成されたHomebrew Formulaです。このファイルは、sisakulintツールの説明、ホームページのURL、リリースされたバージョン、そして異なるアーキテクチャやオペレーティングシステム向けのダウンロードURLとSHA256ハッシュを含んでいます。Formulaの主要な目的は、ユーザーがコマンド一つでsisakulintを簡単にインストールできるようにすることです。

platform(OS)ごとの対応

このFormulaは、MacOSとLinuxの両方をサポートし、さらにはARMとIntelの両アーキテクチャにも対応しています。それぞれの環境に応じて、適切なバイナリファイルのダウンロードURLが指定されています。この柔軟性により、様々なユーザーが自分のシステムに最適なバージョンのsisakulintを入手できます。

インストールプロセス

Formula内のinstallメソッドは、ダウンロードされたバイナリをユーザーのシステム上の適切な場所に配置するための手順を定義しています。このシンプルなステップにより、ユーザーは追加の設定を行うことなく、コマンドラインからsisakulintを直接実行できるようになります。

テストの実行

最後に、Formulaには簡単なテストが含まれており、インストール後にsisakulint -versionを実行することで、ツールが正しくインストールされ動作することを確認できます。

sisakulint.rbファイルを通じて、開発者はリリースプロセスの自動化だけでなく、エンドユーザーに対するインストール体験の簡素化も達成しています。これにより、ユーザーは新しいバージョンがリリースされるとすぐに、最新の機能や修正を簡単に利用できるようになります。

strategy.rb

strategy.rbでは、2つのDownload Strategy関数、GitHubPrivateRepositoryDownloadStrategyGitHubPrivateRepositoryReleaseDownloadStrategyが定義されています。前者は任意のファイルやディレクトリのダウンロードに対応し、後者はGitHubリリースから特定のassetをダウンロードするために最適化されています。これらのクラスは、GitHub APIを通じてリソースのダウンロードURLを生成し、CurlDownloadStrategyを継承しているため、curlコマンドを使用してファイルをダウンロードします。特に、プライベートリポジトリを利用して内部ツールやソフトウェアを配布する際に役立つ肝となる部分はここです。

ultra-supara/homebrew-sisakulint/strategy.rb(パブリックリポジトリ)

# GitHubPrivateRepositoryDownloadStrategy downloads contents from GitHub
# Private Repository. To use it, add
# `:using => :github_private_repo` to the URL section of
# your formula. This download strategy uses GitHub access tokens (in the
# environment variables `HOMEBREW_GITHUB_API_TOKEN`) to sign the request.  This
# strategy is suitable for corporate use just like S3DownloadStrategy, because
# it lets you use a private GitHub repository for internal distribution.  It
# works with public one, but in that case simply use CurlDownloadStrategy.
#
# Usage:
#   class Foo < Formula
#     url "
  require "utils/formatter"
  require "utils/github"

class GitHubPrivateRepositoryDownloadStrategy < CurlDownloadStrategy
  def initialize(url, name, version, **meta)
    super
    parse_url_pattern
    set_github_token
  end


  def parse_url_pattern
    unless match = url.match(%r{https://github.com/([^/]+)/([^/]+)/(\S+)})
      raise CurlDownloadStrategyError, "Invalid url pattern for GitHub Repository."
    end


    _, @owner, @repo, @filepath = *match
  end


  def download_url
    "https://#{@github_token}@github.com/#{@owner}/#{@repo}/#{@filepath}"
  end


  private


  def _fetch(url:, resolved_url:, timeout:)
    curl_download download_url, to: temporary_path
  end


  def set_github_token
    #@github_token = ENV["HOMEBREW_GITHUB_API_TOKEN"]
    #unless @github_token
    #  raise CurlDownloadStrategyError, "Environmental variable HOMEBREW_GITHUB_API_TOKEN is required."
    #end


    validate_github_repository_access!
  end


  def validate_github_repository_access!
    # Test access to the repository
    GitHub.repository(@owner, @repo)
  rescue GitHub::HTTPNotFoundError
    # We only handle HTTPNotFoundError here,
    # becase AuthenticationFailedError is handled within util/github.
    message = <<~EOS
      HOMEBREW_GITHUB_API_TOKEN can not access the repository: #{@owner}/#{@repo}
      This token may not have permission to access the repository or the url of formula may be incorrect.
    EOS
    raise CurlDownloadStrategyError, message
  end
end


# GitHubPrivateRepositoryReleaseDownloadStrategy downloads tarballs from GitHub
# Release assets. To use it, add `:using => :github_private_release` to the URL section
# of your formula. This download strategy uses GitHub access tokens (in the
# environment variables HOMEBREW_GITHUB_API_TOKEN) to sign the request.
class GitHubPrivateRepositoryReleaseDownloadStrategy < GitHubPrivateRepositoryDownloadStrategy
  def initialize(url, name, version, **meta)
    super
  end


  def parse_url_pattern
    url_pattern = %r{https://github.com/([^/]+)/([^/]+)/releases/download/([^/]+)/(\S+)}
    unless @url =~ url_pattern
      raise CurlDownloadStrategyError, "Invalid url pattern for GitHub Release."
    end


    _, @owner, @repo, @tag, @filename = *@url.match(url_pattern)
  end


  def download_url
    "https://#{@github_token}@api.github.com/repos/#{@owner}/#{@repo}/releases/assets/#{asset_id}"
  end


  private


  def _fetch(url:, resolved_url:, timeout:)
    # HTTP request header `Accept: application/octet-stream` is required.
    # Without this, the GitHub API will respond with metadata, not binary.
    curl_download download_url, "--header", "Accept: application/octet-stream", to: temporary_path
  end


  def asset_id
    @asset_id ||= resolve_asset_id
  end


  def resolve_asset_id
    release_metadata = fetch_release_metadata
    assets = release_metadata["assets"].select { |a| a["name"] == @filename }
    raise CurlDownloadStrategyError, "Asset file not found." if assets.empty?


    assets.first["id"]
  end

  def fetch_release_metadata
    release_url = "https://api.github.com/repos/#{@owner}/#{@repo}/releases/tags/#{@tag}"
    res = curl_output release_url
    puts res.stdout
    JSON.parse(res.stdout)
  end
end

以下に、strategy.rbがどのように機能するかを簡潔にまとめます。

GitHub Private Repository Download Strategy

この戦略は、GitHubリポジトリ(プライベートまたは公開)からコンテンツをダウンロードする際に用いられます。strategy.rbファイルに定義されたGitHubPrivateRepositoryDownloadStrategyは、環境変数HOMEBREW_GITHUB_API_TOKENに設定されたアクセストークンを利用して、GitHubのAPIを通じて安全にアクセスし、assetをダウンロードします。このプロセスは、特定のファイルやリリースassetを指定してダウンロードする柔軟性を提供し、内部的に使用するソフトウェアやツールの配布に適しています。

GitHub Private Repository Release Download Strategy

GitHubリリースassetからターボールをダウンロードするために使用される戦略です。URLセクションに:using => :github_private_releaseを追加することで適用されます。この戦略もまた、環境変数に設定されたGitHubアクセストークンを使用し、リクエストに署名しますが、リリースassetのダウンロードに特化しています。

実装の詳細

  • URLパターンの解析
    指定されたURLからリポジトリのオーナー名、リポジトリ名、ファイルパス(またはリリースタグとファイル名)を抽出します。

  • ダウンロードURLの生成
    アクセストークンをURLに含めることで、プライベートリポジトリまたはリリースassetからのダウンロードURLを生成します。

  • asset IDの解決
    リリースダウンロード戦略では、指定されたリリースタグに基づいてリリースassetのメタデータを取得し、必要なファイルのasset IDを解決します。

これらのカスタムダウンロード戦略により、Homebrewを通じてプライベートなGitHubリポジトリやリリースassetから安全にソフトウェアをダウンロードし、インストールすることが可能になります。これは、特にプライベートリポジトリを使用して内部でソフトウェアを配布する必要がある企業や組織にとって価値のある機能です。strategy.rbは、Homebrewの拡張性と柔軟性を示す良い例であり、開発者が独自のニーズに合わせてツールをカスタマイズできるように支援します。

また、GitHub Private Repository Release Download Strategyは、GitHubリリースから特定のassetをダウンロードする際に使用されます。この戦略は、URLセクションに:using => :github_private_releaseを追加することで活用でき、GitHubアクセストークンを使用してリクエストに署名します。この戦略は、プライベートリポジトリのリリースassetを配布する目的に特化しており、リリースバージョンに基づいた配布を可能にします。

これらの戦略に共通するのは、セキュアなダウンロードを可能にすること、およびHomebrew Formula内での簡単な指定方法です。GitHubのプライベートリポジトリやリリースassetを使用して内部的にソフトウェアを配布する際、これらの戦略により、開発者はダウンロードプロセスを簡単かつ安全に管理できます。strategy.rbは開発者が特定のニーズに合わせてツールをカスタマイズできるようにも支援できているかと思います。これにより、配布プロセスが大幅に効率化され、セキュリティと効率のバランスを取りながらニーズに対応できるようになります。

tapリポジトリのrelease.yaml

homebrew tapしてtoken連携をすることによって取得してきたアーティファクトをtapリポジトリに安全にリリースするプロセスの紹介です。まさにマトリョーシカのようですね。

ultra-supara/homebrew-sisakulint/.github/workflows/release.yaml(パブリックリポジトリ)

name: Release

on:
  push:
    branches:
      - 'main'
    tags:
      - 'v*'

permissions:
  contents: write


jobs:
  release:
    runs-on: ubuntu-latest
    steps:
      - name: Download Sisakulint Release
        run: |
          gh release download ${{ github.ref_name }} --repo ultra-supara/sisakulint --pattern 'sisakulint_*_*_*.tar.gz' --pattern 'sisakulint_*_checksums.txt'
          ls -laR
        env:
          GH_TOKEN: ${{ secrets.HOMEBREW_GITHUB_API_TOKEN }}
      - name: Release
        uses: softprops/action-gh-release@v1
        with:
          files: |
            sisakulint_*_*_*.tar.gz
            sisakulint_*_checksums.txt
          fail_on_unmatched_files: true

このワークフローは、特定の条件下でソフトウェアの新しいバージョンをリリースするための自動化された手順を定義します。主に、mainブランチへのプッシュやv*というパターンにマッチするタグがプッシュされた際にトリガーされます。

このワークフローの核心は、ソフトウェアの新しいリリースを効率的かつ確実に管理することにあります。最初のステップでは、gh release downloadコマンドを使用して、指定されたバージョン(GitHubのタグに基づく)のSisakulintリリースをダウンロードします。バイナリファイルとチェックサムファイルの両方を取得することで整合性と安全性を保証しています。

ダウンロードが完了した後、softprops/action-gh-release3rd party アクションを利用して、ダウンロードしたファイルをGitHubリリースとして公開します。このステップでは、マッチしないファイルがある場合に失敗するように設定されており、リリースプロセスの正確性を確保するための重要な安全策となっています。このようにして、開発者はリリースプロセスの一貫性を保ちつつ、新しいバージョンのソフトウェアを迅速に公開することが可能になります。

参考にしたドキュメントなど

https://goreleaser.com/customization/homebrew/

https://qiita.com/mountcedar/items/f10161d4e483aa8c1283#バイナリインストール

https://stackoverflow.com/questions/70572469/get-artifact-from-other-repo

https://rubydoc.brew.sh/Homebrew/Livecheck/Strategy/GithubReleases.html

https://rubydoc.brew.sh/Utils/Curl.html#curl_output-class_method

https://docs.brew.sh/How-to-Create-and-Maintain-a-Tap

Discussion