Overview
buf.gen.yaml で local の protoc コマンドを hook して実行するときに go tool 経由で実行すると個人の環境差異にとらわれずに実行できて便利、という話です。
Motivation
proto からコードを生成するときに、今では buf generate を利用するのが一般的な選択肢ですが、 buf.gen.yaml で protoc のプラグイン(grpc や openapi) を利用するときに、各々の local 環境にこれからのプラグインがインストールされていて、かつバージョンが揃っていないと期待する生成結果を得られないことがあります。
事前に使うライブラリの種類やバージョンを指定して個々人の端末にインストールを行う make を作ることもできますが、結局実行し忘れたりで buf generate が正常に動作しないこともあります。
go tool で解決する
これを解決するために Go1.24 から導入された go tool を利用します。
詳細は以下ですがこれは Go プロジェクトにおける Module の管理の範囲を各種ツールにも広げた機能で、すでに一般的に使われるようになっているかと思います。
結論から言ってしまうと、protoc に関連するツール郡(ほぼ Go 製)を tool としてプロジェクトの依存管理対象に設定し、 buf.gen.yaml の local での実行コマンドを go tool google.golang.org/protobuf/cmd/protoc-gen-go のように設定します。
例えば protoc において grpc, grpc-gateway, openapi を利用するときの go.mod と buf.gen.yaml の plugin 設定は以下のようになります。
# go.mod
tool (
github.com/bufbuild/buf/cmd/buf
github.com/google/gnostic/cmd/protoc-gen-openapi
github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway
google.golang.org/grpc/cmd/protoc-gen-go-grpc
google.golang.org/protobuf/cmd/protoc-gen-go
)
# buf.gen.yaml version: v2 clean: true managed: enabled: true disable: - file_option: go_package_prefix plugins: - local: [go, tool, google.golang.org/protobuf/cmd/protoc-gen-go] out: server/gen opt: - paths=import - local: [go, tool, google.golang.org/grpc/cmd/protoc-gen-go-grpc] out: server/gen opt: - paths=import - local: [go, tool, github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway] out: server/gen opt: - paths=import - local: [go, tool, github.com/google/gnostic/cmd/protoc-gen-openapi] out: openapi opt: - output_mode=source_relative
これで buf generate すると go tool .... 経由で各種プラグインのコマンドが実行されるので、仮にプラグインのコマンドがインストールされ知なくても go tool 経由でプロジェクト内でインストールされ go tool 経由で実行されます。さらにバージョンも go.mod で管理されるのでバージョン違いによる、出力の違い、ということにも頭を悩ませることはありません。
まとめ
Go のプロジェクト限定にはなりますが、go tool で protoc のプラグインを管理するのは有用だなと思いました。