🐁

Go で作成した CLI ツールにバージョンを埋め込む方法

に公開

はじめに

CLI ツールを使う上で切っても切り離せない version コマンド。

> go version
go version go1.25.0 darwin/arm64

自分がインストールしたツールがどのバージョンなのかを確認する上で非常に重要な機能です。
今回はそんな CLI ツールに欠かせない機能を Go で作成した場合にどうやってバージョン情報を埋め込むのかを調べてみました。
全部で4パターン発見したので、実際に利用されているプロジェクトの例を使ってご紹介します。
他にも方法を知っている方がいましたらぜひ共有してください!

定数/const パターン

最もシンプルな管理方法で定数/const として直接コードでバージョンを管理する方法です。

実装は以下のような形式になります。

https://github.com/cheggaaa/pb/blob/v3.1.7/pb.go#L16

※ 参考実例

https://github.com/cheggaaa/pb

最もシンプルではありますが、バージョン情報を自動で更新したい場合などに正規表現や静的解析を用いて対象のコードを書き換える必要があります。

go:embed パターン

Go 1.16 で登場した go:embed 機能を利用してファイルでバージョンを管理する方法です。

https://future-architect.github.io/articles/20210208/

実装は以下のような形式になります。

https://github.com/sibprogrammer/xq/blob/v1.3.0/main.go#L15-L16

https://github.com/sibprogrammer/xq/blob/v1.3.0/version

※ 参考実例

https://github.com/sibprogrammer/xq

定数/const パターンと異なり、ファイルで管理するのでコードの書き換えが定数/const パターンより楽に行えるかと思います。

go build -ldflags -X パターン

ビルド時にバージョン情報を渡し
GoReleaser というツールのドキュメントでも紹介されています。

https://goreleaser.com/cookbooks/using-main.version/

-ldflags を渡すことでビルド時に内部で呼ばれる go tool link にオプションを渡すことができます。

https://pkg.go.dev/cmd/link

成果物のサイズを小さくするために -ldflags="-s -w" を利用することがあると思います。
-X オプションを渡すことで特定のパッケージ内の変数に値を設定することが可能になります。

-X importpath.name=value
Set the value of the string variable in importpath named name to value.
This is only effective if the variable is declared in the source code either uninitialized
or initialized to a constant string expression. -X will not work if the initializer makes
a function call or refers to other variables.
Note that before Go 1.5 this option took two separate arguments.

実装は以下のような形式になります。

https://github.com/patrickdappollonio/kubectl-slice/blob/v1.4.2/app.go#L15

https://github.com/patrickdappollonio/kubectl-slice/blob/v1.4.2/.goreleaser.yml#L18

※ 参考実例

https://github.com/patrickdappollonio/kubectl-slice

これまで紹介してきたパターンと異なりコードの外からバージョン情報を管理することができるのでバージョン更新のためにコードを変更する必要がなくなります。

更新が楽になった一方で go install コマンドでインストールした際にはバージョン情報を埋め込むことができないという欠点があります。

buildinfo パターン

Go 1.18 で登場した buildinfo というパッケージを利用して管理する方法です。

https://pkg.go.dev/debug/buildinfo

https://future-architect.github.io/articles/20220217a/

Git の commit id や commit した時刻等のバージョン管理システム(VCS) 情報と、build したアーキテクチャやOS等のビルド情報が取得できます。
つまり、GitHub などでリリースタグを付与しておけば成果物にバージョン情報が含まれることになります。

実装は以下のような形式になります。

https://github.com/kubecolor/kubecolor/blob/v0.5.1/main.go#L34-L47

※ 参考実例

https://github.com/kubecolor/kubecolor

この方法であれば go install コマンドを利用してインストールしたとしてもバージョン情報が埋め込まれます。

おわりに

4つのパターンについてご紹介してみました。
これから Go で CLI ツールを作るという方は buildinfo を検討してよいのではないかと思います。

Discussion