スタディサプリでソフトウェアエンジニアやエンジニアリングマネージャをやっている @pankona です。
Go 1.24 でツールのバージョン管理が便利になりそうという話をします。
(Go 1.23 現在) go install
という便利なコマンドがある
Go 1.24 の話をする前に、go install
というコマンドの現状について簡単に触れておきます。
Go で書かれたツールのうち、所定の条件を満たすものは go install
コマンドでインストールすることができます。たとえば goimports であれば、
go install golang.org/x/tools/cmd/goimports@latest
というコマンドを実行することで我々のPCにインストールされ、使うことができるようになります。たいへん簡単で便利です。
インストールするツールのバージョンを指定したい場合はどのようにすればいいでしょうか。バージョンは末尾のアットマークより後の部分で指定することができます。たとえばコマンドを実行した時点での最新バージョンを入手したければ、先述のコマンドのように末尾に @latest
をつけます。最新バージョンではなく所望のバージョンを指定したい場合は、@v1.2.3
のように適当なバージョンを指定することもできます (バージョンを示す値として、コミットハッシュやブランチ、タグなどが指定できます) 。
go install
で導入するツールのバージョンは go.mod で管理してもらえなくて不便
ところで、renovate1 や dependabot2 というプロダクトをご存知でしょうか。renovate や dependabot は、我々が作っているソフトウェアが依存しているライブラリについて、新しいバージョンがリリースされていたら自動的にプルリクエストを作り、ライブラリの更新をサポートしてくれます。renovate や dependabot は Go もサポートしていて、go.mod (各 Go プロジェクトで用いられている依存ライブラリが記載されているファイル) の記載内容を元にしてライブラリ更新のためのプルリクエストを作ってくれます。
さて、go install
コマンドでインストールするツールに関しても、依存ライブラリと同様に新しいバージョンが出たらバージョンを更新するプルリクエストを出してもらえると便利です。しかし go install
コマンドで導入したツールについては go.mod にバージョンが基本的には書かれません。これは go.mod はあくまで各 Go プロジェクトのビルドを行うために必要なライブラリのバージョンを記載するための場所であり、ビルドに直接関わりのないツール関連の記載は最終的には消してしまうことが理由です (go mod tidy をしたときに消えます) 。npm で言うところの devDependencies の仕組みが go.mod にはないということです。
tools.go を使うというアプローチ
そこで、tools.go (のような何らかのファイル) を用いて go mod tidy してもツールの依存を go.mod から消させないようにするアプローチが公式 Wiki で紹介されています。
当該 Wiki では、「ブランクインポート」を用いたテクニックが紹介されています。ブランクインポートとは、以下のような形式のインポート文を指します。下記の例では golang.org/x/tools/cmd/goimports
をブランクインポートしています。
package tools import ( _ "golang.org/x/tools/cmd/goimports" )
ソースコード内でブランクインポートされたパッケージは go mod tidy しても go.mod から消えなくなります。この性質を使ってツールのパッケージをどこかでブランクインポートしておき、ツールのバージョンをも go.mod で管理しちゃおう!というやり方です。
これは公式から案内されているアプローチではありますが、ややハックなやり方に見えないこともありません (実際、公式な文書でも「workaround である」と紹介されていたりもします) 。
とはいえ本手法はそれなりに古く (ツールの導入に go get
を用いていた Go 1.16 よりも前) から有効な手段であり、かつ2024年12月現在でも健在なやり方です。
本手法でツールを導入する場合、概ね以下のような手順を踏みます3。
- go.mod があるプロジェクトで、
go install <ツールのパッケージ>@latest
のようなやり方でツールを導入する- この段でツールとツールの依存関係にあるライブラリのバージョンが go.mod に記載される
- tools.go にツールのパッケージをブランクインポートしておく
- この段で go mod tidy しても ツールに関連した依存ライブラリが go.mod から消えなくなる
- 次から
go install <ツールの URL>
とすれば、go.mod に記載されたバージョンのツールがインストールされる
Go 1.24 からより便利な仕組みが公式から提供される
workaround じゃなくてもっと便利な仕組みを入れようぜ!という掛け声があったかどうかは分かりませんが、Go 1.24 から tools.go によるツールのバージョン管理と同じようなことを達成するための 専用の仕組み が導入されるようです。関連 issue は以下です。 github.com
ちょっと前まで Go 1.24 に入るかどうか微妙な感じだった気がしますが、どうやら Go 1.24 に入っていくような雰囲気が濃厚なように見えます。
以下は Go 1.24 のリリースノートになる予定の内容です。こちらにもすでに関連情報が載っていますね…!
リリースノートを軽くつまんで読んでみると、どうやら以下のような感じになりそうです (最終的な内容は変わる可能性があります) 。
go get -tool golang.org/x/tools/cmd/stringer
のようにしてツールを導入する- インストールしたいツールのパッケージを指定して
go get -tool
コマンドを実行するようですね。 - すると go.mod 内の "tool" という欄にツールに関する情報 (リビジョン情報と依存関係) が書き込まれるらしいです。
- バージョン管理の方法は既存の go.mod と同じような感じにできそう (require、replace、exclude なども使えるようだ) なことも書いてあります。
- インストールしたいツールのパッケージを指定して
- 導入したツールを実行するには
go tool stringer
のようにする- 実行するときにはツールのパッケージのフルパスを指定する必要はなく、末尾の部分だけ指定すれば良い模様
- go.mod に書き込まれたツールを一式インストールしたいときは
go install tool
とする- 全部のツールを
$GOBIN
に突っ込みたいときはこれをやるようです。
- 全部のツールを
- ツール一式のバージョンを更新したいときは
go get -u tool
とする- 便利そう。
まとめ
- Go 1.24 からツールのバージョン管理を go.mod で行う仕組みが導入されるぞ
go get -tool <ツールのパッケージ>
で導入し、go tool <ツール名>
みたいな感じで実行するようになるらしい
- Go 1.24 からはもう tools.go を使った workaround が不要になりそうだぞ…!
Go 1.24 のリリースは 2025年2月くらいです。首を長くして待っておきましょう。
- https://docs.renovatebot.com↩
- https://docs.github.com/ja/code-security/dependabot/working-with-dependabot↩
-
ちなみに、このやり方で入れるツールと、このやり方をせずに
go install <ツールの URL>@latest
のようにしてツールを入れる場合とでは、微妙に異なる組み合わせのライブラリが用いられる場合があります。稀にビルドが通らないなどの問題を引き起こす場合もあります。↩