作为 MacOS 用户,自然是离不开 Homebrew,因为它是 MacOS 的安装包管理工具。本文主要介绍如果将一个 Go 的二进制包发布到 Homebrew Tap 上,然后可以使用 brew install 的方式进行安装 Go 的二进制包。

同时本文会讲解到两种发布方式,一种是纯手动的方式,另一种则是基于 GitHub Actions + GoReleaser 的自动化发布。

手动发布

创建 Formula

通过命令在本地创建 Formula,后面的链接指向我们需要发布的二进制文件

brew create https://github.com/k8scat/Articli/releases/download/v0.2.2/acli-darwin-arm64.tar.gz

通过这个命令,我们可以得到一个 articli.rb 文件,放在 /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core/Formula/ 目录下。

修改 Formula

下面是原文件内容

# Documentation: https://docs.brew.sh/Formula-Cookbook
#                https://rubydoc.brew.sh/Formula
# PLEASE REMOVE ALL GENERATED COMMENTS BEFORE SUBMITTING YOUR PULL REQUEST!
class Articli < Formula
  desc "Manage content in multi platforms."
  homepage ""
  url "https://github.com/k8scat/Articli/releases/download/v0.2.2/acli-darwin-arm64.tar.gz"
  sha256 "c04aad6e2ca3ccf1d902d0f4f2bbc4422c67fa355a166cc3b0dac2fd9f420151"
  license "MIT"

  # depends_on "cmake" => :build

  def install
    # ENV.deparallelize  # if your formula fails when building in parallel
    # Remove unrecognized options if warned by configure
    # https://rubydoc.brew.sh/Formula.html#std_configure_args-instance_method
    system "./configure", *std_configure_args, "--disable-silent-rules"
    # system "cmake", "-S", ".", "-B", "build", *std_cmake_args
  end

  test do
    # `test do` will create, run in and delete a temporary directory.
    #
    # This test will fail and we won't accept that! For Homebrew/homebrew-core
    # this will need to be a test that verifies the functionality of the
    # software. Run the test with `brew test Articli`. Options passed
    # to `brew install` such as `--HEAD` also need to be provided to `brew test`.
    #
    # The installed folder is not in the path, so use the entire path to any
    # executables being tested: `system "#{bin}/program", "do", "something"`.
    system "false"
  end
end

简化这个文件

# Documentation: https://docs.brew.sh/Formula-Cookbook
#                https://rubydoc.brew.sh/Formula
# PLEASE REMOVE ALL GENERATED COMMENTS BEFORE SUBMITTING YOUR PULL REQUEST!
class Articli < Formula
  desc "Manage content in multi platforms."
  homepage "https://github.com/k8scat/Articli"
  url "https://github.com/k8scat/Articli/releases/download/v0.2.2/acli-darwin-arm64.tar.gz"
  sha256 "c04aad6e2ca3ccf1d902d0f4f2bbc4422c67fa355a166cc3b0dac2fd9f420151"
  license "MIT"
  version "0.2.2"

  def install
    bin.install "acli"
  end
end

由于这里下载的是一个二进制包,所以直接在 install 中填上 bin.install "acli" 即可。

创建个人的 Homebrew Tap

在 GitHub 上新建一个名为 homebrew-tap 的公开仓库,这里我创建的便是 k8scat/homebrew-tap。然后将上面修改后的 articli.rb 文件上传到这个仓库中,这就创建好了我们的个人 Homebrew Tap。

使用 Homebrew Tap

# 添加 Homebrew Tap
# k8scat/tap 指向的是我们刚刚创建的个人 Homebrew Tap: k8scat/homebrew-tap
brew tap k8scat/tap
# 安装二进制包
brew install articli

# 一条命令安装,这会自动添加 Homebrew Tap
brew install k8scat/tap/articli

安装顺序

如果 homebrew/core 和 个人的 Homebrew Tap 上出现同名包,默认会安装 homebrew/core 上的包,但我们可以指明 Homebrew Tap:

# 指明 Homebrew Tap 进行安装
brew install k8scat/tap/articli

我们还有另一种方式修改这个顺序:使用 brew pin 命令可以将个人的 Homebrew Tap 的优先顺序提高,这样就会优先安装个人的 Homebrew Tap 上的包。

自动化发布

上面讲的通过 brew create 命令创建 Formula 并手动修改文件,然后上传到 Homebrew Tap 仓库中,这种方式可以基于 GitHub Actions + GoReleaser 做到自动化发布。

添加 .goreleaser.yml 文件

下面的配置会告诉 GoReleaser 如何生成 formula

brews:
  - name: acli
    tap:
      owner: k8scat
      name: homebrew-tap
      token: "{{ .Env.HOMEBREW_TOKEN }}"

    url_template: "https://github.com/k8scat/articli/releases/download/{{ .Tag }}/{{ .ArtifactName }}"
    # Git author used to commit to the repository.
    # Defaults are shown.
    commit_author:
      name: goreleaserbot
      email: [email protected]
    homepage: "https://github.com/k8scat/articli"
    description: "Manage content in multi platforms."

    # Packages your package depends on.
    dependencies:
      - name: git

    # So you can `brew test` your formula.
    # Default is empty.
    test: |
      system "#{bin}/acli version"      
    # Custom install script for brew.
    # Default is 'bin.install "program"'.
    install: |
      bin.install "acli"
      # Install bash completion
      output = Utils.safe_popen_read("#{bin}/acli", "completion", "bash")
      (bash_completion/"acli").write output
      # Install zsh completion
      output = Utils.safe_popen_read("#{bin}/acli", "completion", "zsh")
      (zsh_completion/"_acli").write output      

GitHub Actions

下面需要设置 Homebrew Tap 仓库的 GitHub Token,这里我们设置了 HOMEBREW_TOKEN

name: Release

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

jobs:
  release:
    name: Release
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v2

      - name: Set up Go
        uses: actions/setup-go@v2
        with:
          go-version: 1.17

      - name: Validates GO releaser config
        uses: goreleaser/goreleaser-action@v2
        with:
          args: check

      - name: Run GoReleaser
        uses: goreleaser/goreleaser-action@v2
        with:
          version: latest
          args: release --rm-dist
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          # Token for homebrew-tap repo: https://github.com/k8scat/homebrew-tap
          HOMEBREW_TOKEN: ${{ secrets.HOMEBREW_TOKEN }}

这样我们就可以通过创建一个标签进行发布了,新发布的 formula 就会自动添加到 Homebrew Tap 仓库中。

参考项目

k8scat/Articli