iTranslated by AI

The content below is an AI-generated translation. This is an experimental feature, and may contain errors. View original article
🍺

How to Distribute Custom CLI Tools via brew install with GoReleaser Automation

に公開

Eyecatch

Introduction

When you create your own CLI tool, have you ever worried about how to distribute it?

It's a hassle to explain the process of "downloading from GitHub Releases, setting up the path..." every single time, isn't it?

By using Homebrew Tap, users can install it with just the following commands:

brew tap your-name/tap
brew install your-name/tap/your-tool

In this article, I will explain the steps for beginners to enable the distribution of custom CLI tools via brew install.

Overview

The mechanism of Homebrew Tap is as follows:

┌─────────────────┐     ┌─────────────────┐     ┌─────────────────┐
│  Tool           │     │  GitHub         │     │  homebrew-tap   │
│  Repository     │ ──▶ │  Releases       │ ◀── │  Repository     │
│  (Source Code)  │     │  (Binaries)     │     │  (Formula File) │
└─────────────────┘     └─────────────────┘     └─────────────────┘


                                               brew tap & install
  1. Create a release in the tool's repository and upload the binaries.
  2. Place the Formula file in the Homebrew-tap repository.
  3. Users install via brew tap and brew install.

Step 1: Create a Homebrew-tap Repository

Create a repository named homebrew-tap on GitHub.

Directory Structure

homebrew-tap/
├── Formula/
│   └── your-tool.rb    # Formula file
└── README.md

README.md Example

# Homebrew Tap

Homebrew formulae for your projects.

## Installation

\`\`\`bash
brew tap your-name/tap
\`\`\`

## Available Formulae

### your-tool

Your tool description here.

\`\`\`bash
brew install your-name/tap/your-tool
\`\`\`

Step 2: Write the Formula File

Create Formula/your-tool.rb. Here is an example based on actual usage.

class YourTool < Formula
  desc "Your tool description"
  homepage "https://github.com/your-name/your-tool"
  license "MIT"
  version "1.0.0"

  on_macos do
    on_intel do
      url "https://github.com/your-name/your-tool/releases/download/v1.0.0/your-tool_1.0.0_darwin_amd64.tar.gz"
      sha256 "sha256 hash here"
    end
    on_arm do
      url "https://github.com/your-name/your-tool/releases/download/v1.0.0/your-tool_1.0.0_darwin_arm64.tar.gz"
      sha256 "sha256 hash here"
    end
  end

  on_linux do
    on_intel do
      url "https://github.com/your-name/your-tool/releases/download/v1.0.0/your-tool_1.0.0_linux_amd64.tar.gz"
      sha256 "sha256 hash here"
    end
    on_arm do
      url "https://github.com/your-name/your-tool/releases/download/v1.0.0/your-tool_1.0.0_linux_arm64.tar.gz"
      sha256 "sha256 hash here"
    end
  end

  def install
    bin.install "your-tool"
  end

  test do
    system "#{bin}/your-tool", "--version"
  end
end

How to obtain the sha256 hash

You can obtain it by downloading the release tar.gz file and running the following command:

shasum -a 256 your-tool_1.0.0_darwin_arm64.tar.gz

Step 3: Automate with GoReleaser (Recommended)

It is tedious to update the Formula manually every time. By using GoReleaser, you can automatically update the Homebrew-tap during the release process.

.goreleaser.yaml Configuration

version: 2

builds:
  - id: your-tool
    main: ./cmd/your-tool
    binary: your-tool
    env:
      - CGO_ENABLED=0
    goos:
      - linux
      - darwin
    goarch:
      - amd64
      - arm64

archives:
  - id: your-tool
    formats:
      - tar.gz
    name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}"

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

GitHub Actions Configuration

Create .github/workflows/release.yml.

name: Release

on:
  push:
    tags:
      - 'v*'

permissions:
  contents: write

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

      - uses: actions/setup-go@v5
        with:
          go-version-file: go.mod

      - name: Run GoReleaser
        uses: goreleaser/goreleaser-action@v6
        with:
          distribution: goreleaser
          version: latest
          args: release --clean
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          HOMEBREW_TAP_GITHUB_TOKEN: ${{ secrets.HOMEBREW_TAP_GITHUB_TOKEN }}

Personal Access Token Configuration

  1. Create a new token from GitHub Settings > Developer settings > Personal access tokens.
  2. Grant the repo scope.
  3. Register it as HOMEBREW_TAP_GITHUB_TOKEN in the tool repository's Settings > Secrets > Actions.

Now, every time you push a tag, the Homebrew-tap will be updated automatically.

Step 4: Try it out

Once everything is set up, users can install it using the following commands:

# Add the tap (first time only)
brew tap your-name/tap

# Install
brew install your-name/tap/your-tool

# Or in one line
brew install your-name/tap/your-tool

Updating is also easy.

brew upgrade your-name/tap/your-tool

Actual Examples

In the Homebrew-tap I maintain, I distribute the following tools:

  • glowm — A Markdown viewer with Mermaid support
  • lazyccg — An AI coding session monitoring dashboard
  • gh-attach — A tool for uploading images to GitHub Issues/PRs

https://github.com/atani/homebrew-tap

Summary

  • Create a homebrew-tap repository and place the Formula file in Formula/.
  • Calculate the sha256 hash from the release binaries.
  • Use GoReleaser to automate the process.
  • Users can install your tool with just brew tap + brew install.

Try using Homebrew Tap to make the distribution of your custom CLI tools more convenient!

Discussion