Go で書いた CLI ツールを GitHub Actions と GoReleaser を使って brew コマンドでインストールできるようにした
2021-02-05Go でちょっとした CLI ツールを作ったので、 GitHub Actions と GoReleaser を使って brew コマンドでインストールできるようにしてみました。その時の手順をまとめておきます。
目次
概要
GitHub Actions と GoReleaser を使って Go で書いた CLI ツールを brew コマンドでインストールできるようにします。
実際にこの方法で公開しているツールがあるので、これを公開するときにとった手順を書いていきます。
ちなみにこれは、 URL を渡すといろんな形式 (Markdown/HTML/QR コード) でリンクを生成してくれるツールです。
やったこと
やったことは下記の通りです。
- homebrew-genlink リポジトリを作成
- genlink リポジトリを作成
- CLI ツールの実装
CREDITS
ファイルを作成.github/workflows/release.yml
と.goreleaser.yml
を作成- タグ付けして Push
- brew コマンドでインストール
それぞれ詳細を書いていきます。
1. homebrew-genlink リポジトリを作成
まずは GitHub で homebrew-genlink というリポジトリを作ります。
これは、作成したツールを brew tap を使用して配布するために必要なリポジトリとなります。
brew コマンドでインストールできるツールは、基本的には Homebrew 公式の Homebrew/homebrew-core に追加されている必要があります。ただし、公式リポジトリへ追加してもらうとなると、 PR を作ってレビューしてもらってマージしてもらう流れになり、公開までに時間がかかります。また、バージョンアップのたびにこのフローを踏む必要があります。
brew tap を使うと、公式以外のリポジトリにあるツールを brew コマンドでインストールできるようになります。その際、対象となるリポジトリの命名規則が「homebrew-{hoge}」となっています。
このリポジトリはあくまでも brew tap で認識させるためのリポジトリで、このリポジトリでソースコードを管理するわけではありません。
brew/Taps.md at master · Homebrew/brew
2. genlink リポジトリを作成
実際にソースコードを管理するリポジトリ genlink を作成します。リポジトリを作成する際、 LICENSE
を一緒に作っておきます。(これは任意です)
2-1. CLI ツールの実装
ツールを実装します。
Go で CLI ツールを作成する際、標準ライブラリの flag
パッケージを使用することでコマンド実行時のパラメータをいい感じに扱う事ができます。 main.go
はこんな感じになってます。
package main
import (
"flag"
"fmt"
"os"
)
var (
targetUrl *string = flag.String("u", "", "Type of link to output")
genType *string = flag.String("t", "md", "Type of link to output")
outDir *string = flag.String("o", "", "Directory to output QR code")
version string
revision string
)
func usage() {
format := `
_ _ _
__ _ ___ _ __ | (_)_ __ | | __
/ _' |/ _ \ '_ \| | | '_ \| |/ /
| (_| | __/ | | | | | | | | <
\__, |\___|_| |_|_|_|_| |_|_|\_\
|___/ Version: %s-%s
Usage:
genlink [flags] [values]
Flags:
-u (required) URL
-t Type of link to output
md: Markdown (default)
html: HTML a tag
html-bl: HTML a tag with 'target="_blank"'
qr: QR code image
-o Absolute path to directory to output QR code
Use this flag in combination with '-t qr'
Default is current directory
Author:
michimani <michimani210@gmail.com>
`
fmt.Fprintln(os.Stderr, fmt.Sprintf(format, version, revision))
}
func main() {
flag.Usage = usage
flag.Parse()
os.Exit(run())
}
func run() int {
res, err := Generate(*targetUrl, *genType, *outDir)
if err != nil {
fmt.Println(err.Error())
return 1
}
fmt.Println(res)
return 0
}
flag.Usage
に代入した関数は、 -h
または --help
オプションを付けて実行した際に実行されるので、コマンドの使い方を出力するようにしています。
version string
revision string
宣言だけして初期化していないこの変数については後で説明します。
ちなみに 「genlink」アスキーアートは figlet
コマンドで生成しました。 macOS であれば brew install figlet
でインストールできます。
2-2. CREDITS
ファイルを作成
Go で作成した (Go に限らずですが) ツールを配布する際、そのツール内で使用したパッケージやライブラリのライセンス情報をバイナリに同梱する必要があります。Go の場合、サードパーティのパッケージだけでなく標準パッケージについてもライセンス情報を含める必要があります。これについてはこのあたりで議論されています。
Standard library licensing question · Issue #19893 · golang/go
ただ、使用しているパッケージのライセンス情報をすべて確認するのは面倒です。そこで便利なのが、 gocredits というツールです。
Songmu/gocredits: creates CREDITS file from LICENSE files of dependencies
brew または go get でインストールし、プロジェクトのルートディレクトリで gocredits . > CREDITS
を実行するだけで依存パッケージのライセンス情報が CREDITS
ファイルに出力されます。
2-3. .github/workflows/release.yml
と .goreleaser.yml
を作成
.github/workflows/release.yml
は GitHub Actions を起動するためのファイルです。これについては GoReleaser のドキュメント内にサンプルがあり、ほぼそのまま使用することができます。
今回はこのサンプルを下記のように変更しています。
name: release
on:
push:
tags:
- "v[0-9]+.[0-9]+.[0-9]+"
jobs:
goreleaser:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
with:
fetch-depth: 1
- name: Setup Go
uses: actions/setup-go@v2
with:
go-version: 1.15
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v2
with:
version: latest
args: release --rm-dist
env:
GITHUB_TOKEN: ${{ secrets.ACCESS_TOKEN }}
サンプルからの主な変更点を書いておきます。
まずは、GitHub Actions の起動条件を、tag が Push されたときにしています。
on:
push:
tags:
- "v[0-9]+.[0-9]+.[0-9]+"
続いて、 GITHUB_TOKEN
に設定する値の名前を secrets.ACCESS_TOKEN
に変更しています。これは GITHUB_TOKEN
のまま実行した際に Actions で下記のようなエラーになったからです。
⨯ release failed after 45.53s error=homebrew tap formula: failed to publish artifacts: PUT https://api.github.com/repos/michimani/homebrew-genlink/contents/Formula/genlink.rb: 403 Resource not accessible by integration []
Error: The process '/opt/hostedtoolcache/goreleaser-action/0.155.0/x64/goreleaser' failed with exit code 1
この Actions では、後に説明する .goreleaser.yml
で作成した Homebrew 用の formula ファイルを、最初に作成した homebre-genlink リポジトリに Push します。その際に権限がないと怒られています。
この対処法として、 GitHub の Settings > Developer settings > Personal access tokens から新たに token を作成し、 genlink リポジトリの Settings > Secrets から名前をつけて追加します。このときの名前として GITHUB_TOKEN
は使用できないため ACCESS_TOKEN
として追加しています。必要 scope は、とりあえず repo にチェックが入っていれば OK です。
.goreleaser.yml
は、ビルドのオプションや formula の配置場所などを指定しています。内容は下記の通り。
project_name: genlink
env:
- GO111MODULE=on
before:
hooks:
- go mod tidy
builds:
- main: .
binary: genlink
ldflags: -s -w -X main.version={{.Version}} -X main.revision={{.ShortCommit}} -X main.date={{.Date}}
archives:
- replacements:
darwin: darwin
linux: linux
windows: windows
amd64: x86_64
files:
- LICENSE
- CREDITS
release:
prerelease: auto
brews:
- tap:
owner: michimani
name: homebrew-genlink
folder: Formula
homepage: 'https://github.com/michimani/genlink'
description: 'Generates the URL link in various formats'
license: "MIT"
内容について少し解説。
builds:
- main: .
binary: genlink
ldflags: -s -w -X main.version={{.Version}} -X main.revision={{.ShortCommit}} -X main.date={{.Date}}
この部分で Go のバイナリをビルドしていますが、その際に ldflags
を指定して main パッケージの変数 version
、 revision
、 date
に値を埋め込んでいます。ツールの実装の部分で触れましたが、宣言だけしていた変数はここで値を埋め込むために宣言していたわけです。
こうすることで、ツール内でバージョンやリビジョン情報を表示する際にもソースコードの変更は必要なく、ビルド時に値を埋め込むことができます。
brews:
- tap:
owner: michimani
name: homebrew-genlink
folder: Formula
homepage: 'https://github.com/michimani/genlink'
description: 'Generates the URL link in various formats'
この部分で、作成した formula を配置するリポジトリとディレクトリを指定しています。実際に配置されるのは .rb
ファイルで、中身は下記のような内容です。
# typed: false
# frozen_string_literal: true
# This file was generated by GoReleaser. DO NOT EDIT.
class Genlink < Formula
desc "Generates the URL link in various formats"
homepage "https://github.com/michimani/genlink"
version "0.1.3"
license "MIT"
bottle :unneeded
if OS.mac?
url "https://github.com/michimani/genlink/releases/download/v0.1.3/genlink_0.1.3_darwin_x86_64.tar.gz"
sha256 "9a4e7ec00e526565578f8b39a5f1d7253bb91b26277d6064b57ff3241d767b75"
end
if OS.linux? && Hardware::CPU.intel?
url "https://github.com/michimani/genlink/releases/download/v0.1.3/genlink_0.1.3_linux_x86_64.tar.gz"
sha256 "c7ed041d314c16906711bc536ab82a8b170d9c83b7f057bfcfbe1926d9c75d7b"
end
if OS.linux? && Hardware::CPU.arm? && Hardware::CPU.is_64_bit?
url "https://github.com/michimani/genlink/releases/download/v0.1.3/genlink_0.1.3_linux_arm64.tar.gz"
sha256 "6afa8842542b8ee55833b5af5b9b3b7542b102274c1fe9bfd31408451555dd1f"
end
def install
bin.install "genlink"
end
end
.goreleaser.yml
について、その他のオプションについては公式ドキュメントまたは goreleaser のリポジトリを参照してください。
2-4. タグ付けして Push
あとは、ローカルから Push する際に tag をつけて、 tag も一緒に Push します。
$ git tag v0.1.0
$ git push origin main:main --tag
念のため Actions が起動して正常終了することを確かめておきます。
3. brew コマンドでインストール
Actions が正常に動けば、あとは brew コマンドでインストールするだけです。Homebrew の公式リポジトリに追加されているツールであれば brew install <ツール名>
でインストールできますが、 brew tap を使用した配布の場合は下記のようなコマンドでインストールします。
$ brew install michimani/genlink/genlink
...
...
🍺 /usr/local/Cellar/genlink/0.1.3: 4 files, 5.7MB, built in 3 seconds
$ genlink -h
_ _ _
__ _ ___ _ __ | (_)_ __ | | __
/ _' |/ _ \ '_ \| | | '_ \| |/ /
| (_| | __/ | | | | | | | | <
\__, |\___|_| |_|_|_|_| |_|_|\_\
|___/ Version: 0.1.3-53fc163
Usage:
genlink [flags] [values]
Flags:
-u (required) URL
-t Type of link to output
md: Markdown (default)
html: HTML a tag
html-bl: HTML a tag with 'target="_blank"'
qr: QR code image
-o Absolute path to directory to output QR code
Use this flag in combination with '-t qr'
Default is current directory
Author:
michimani <michimani210@gmail.com>
ツール名を指定する部分が異なるだけで、アップデートやアンインストールについても同様です。
# アップデート
$ brew upgrade michimani/genlink/genlink
# アンインストール
$ brew uninstall michimani/genlink/genlink
まとめ
Go でちょっとした CLI ツールを作ったので、 GitHub Actions と GoReleaser を使って brew コマンドでインストールできるようにしてみた話でした。
配置する YAML ファイルについてはほぼコピペで使えるので、結構簡単に公開できます。
参考にさせていただいたブログに
みなさんも是非こんなものがあったらいいなという CLI ツールを同様の手順で作成、公開して、 GitHub Star 5000兆個を目指してください。
と書かれていたのでこのツールで Star 5000兆個目指したいと思います。
参考にした記事
下記の記事を参考にさせていただきました。ありがとうございます。
- goreleaserでGitHub Actionsから簡単にオレオレCLIをbrew installできるようにする - Qiita
- GoReleaser+GithubActionsを使って、releaseファイルのアップロードとhomebrew対応を自動で行う - 年中アイス
- git switch からはじめる CLI ツール作成
comments powered by Disqus