🌊

プライベートなツールをGitHub Releaseで配布してbrewでインストールする - brew 4 版

2023/04/13に公開

背景

独自のhomebrew tapを用意して、そこから認証の必要なGitHub RepositoryのReleaseからバイナリを配布していた。
昔はbrew公式の機能でインストールができていたができなくなってしまった。
その対応方法として下記があった。
https://qiita.com/eielh/items/812206e77696f947396f
しかし、

これを解決したい。

解決方法

選択肢

  1. GitHub Packges や Amazon S3などから配布する
  2. GitHub Release用のDownloadStrategyを用意する

本記事では DownloadStrategy をbrew version 4対応方法について記述する。
これは上記ブログ記事と同じ手段で、formulaeを変更なしで利用できる利点があります。
長期的にはGitHub Packagesを使うほうがよさそうだがそれは今後検討する。

DownloadStrategyの実装例

# privateなGitHub ReppositoryのReleaseからダウンロードするためのdownload strategy
# @see https://docs.brew.sh/Formula-Cookbook#specifying-the-download-strategy-explicitly
class GitHubPrivateRepositoryReleaseDownloadStrategy < CurlDownloadStrategy

  def initialize(url, name, version, **meta)
    meta[:headers] ||= []
    token = GitHub::API.credentials
    meta[:headers] += ["--header", "Authorization: token #{token}"]
    meta[:headers] += ["--header", "Accept: application/octet-stream"]

    parse_url_pattern(url)
    super(download_url, name, version, **meta)
  end

  def parse_url_pattern(url)
    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)
    @tag = URI.decode(@tag)
  end

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

  private
  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
    GitHub.get_release(@owner, @repo, @tag)
  end
end

これを定義したファイルを読み込みし、formulaeで指定すればよい。

require_relative '../lib/download_strategy'

class XXX < Formula
  desc "xxx"
  homepage "https://example.com/xxx/xxx"
  url "https://example.com/xxx/xxx", :using => GitHubPrivateRepositoryReleaseDownloadStrategy
end

もうちょっと詳しく

brew 4の CurlDownloadStrategy では _fetchが呼ばれる前に 認証の必要urlへのアクセスが発生してしまって失敗していた。そのため、それよりも早く認証情報の付与と実際のダウンロードurlを生成することで解決している。
これらの処理をinitializeメソッドで行うことで、この問題が解決される。

参考情報

DownloadStrategyについて
https://docs.brew.sh/Formula-Cookbook#specifying-the-download-strategy-explicitly

DownloadStrategyの実装
https://github.com/Homebrew/brew/blob/dcc44f164d914e08f27f5154346c35464749a8c9/Library/Homebrew/download_strategy.rb

Discussion