この記事はOpenSaaS Studio Advent Calendar 2019、11日目の記事です。3日目はPython+Bazelでしたが、今回はGolang+Bazel。
はじめに
GolangのプロジェクトでBazelを使う場合、依存ライブラリの二重管理が必要になることがあります。
二重管理は変更の反映漏れにつながったり管理方法が理解しづらくなるため基本的には避けるべきですが、上手くハンドリングできればビルド速度などBazelのメリットを享受しつつ問題なく運用できます。
この記事では、私がジョインしているプロジェクトでこの課題にどう対応したのか紹介します。
依存ライブラリの二重管理
BazelではWORKSPACEというファイルで依存ライブラリを定義します(マクロに定義してWORKSPACEでそのマクロを呼ぶ方法もあります)が、Golangのアプリを開発する場合にはIDEの対応状況やワークフローなどを考慮してgo.modに依存ライブラリを定義することがほとんどだと思います。
その場合、以下のように複数のファイルに同じ依存ライブラリの定義が存在するため二重管理が必要になります。
go_repository(
name = "com_google_cloud_go",
importpath = "cloud.google.com/go",
sum = "h1:0sMegbmn/8uTwpNkB0q9cLEpZ2W5a6kl+wtBQgPWBJQ=",
version = "v0.44.3",
)
require (
cloud.google.com/go v0.44.3
)
二重管理の運用
go.modの変更をWORKSPACEに反映する
一般的な開発フローではIDEなどによりまずgo.modが変更され、その変更をWORKSPACEに反映するためにGazelleが使われます。
READMEの記載を参考にセットアップすると、以下のコマンドでgo.modからWORKSPACEに変更が反映されます。
bazel run //:gazelle -- update-repos -from_file=go.mod
ただし、このコマンドでは以下の問題があります。
- WORKSPACEの定義がオリジナルな依存ライブラリとGazelleで生成された依存ライブラリが同じファイルで管理される
- go.modから削除されたライブラリがWORKSPACEから削除されない
Gazelleで生成する依存ライブラリ定義を隔離する
Gazelleは生成対象の依存ライブラリをマクロとして出力する機能があり、この機能を利用することでWORKSPACEから分離することができます。
bazel run //:gazelle -- update-repos -from_file=go.mod -to_macro=repositories.bzl%go_repositories
to_macro
フラグで出力先を指定しており、この例だとrepositories.bzl
にgo_repositories
という名前で依存ライブラリを定義するためのマクロが生成されます。
生成されたマクロは以下のようにWORKSPACEで呼び出せば依存ライブラリの定義がロードされます。
### Below is the auto-generated dependency macro from go.mod.
# gazelle:repository_macro repositories.bzl%go_repositories
load("//:repositories.bzl", "go_repositories")
go_repositories()
不要になったライブラリの削除
これもフラグがあるのでそれを指定するだけです。
bazel run //:gazelle -- update-repos -from_file=go.mod -to_macro=repositories.bzl%go_repositories -prune=true
prune
フラグです。
ただし、このフラグを設定するとgo.modで解決できないライブラリのgo_repository
が全て削除されてしまいます。
そのままだとPGVなどが削除されてしまうため、もし同様のライブラリ定義があればkeepコメントで削除されないようにしてください。
### Protoc-gen-validate
# keep
# It is used in order to generate validation code from proto, not defined in go.mod.
go_repository(
name = "com_github_envoyproxy_protoc_gen_validate",
importpath = "github.com/envoyproxy/protoc-gen-validate",
tag = "v0.1.0",
)
まとめ
- GolangとBazelで依存ライブラリを管理するためのファイルが別になっており、二重管理が必要
- アプリを開発する時はgo.modを変更
- Gazelleを使ってgo.modの変更をBazel側に反映し、不要になったライブラリも削除
この記事では以上の内容を説明してきましたが、他の言語でBazelを使う場合やデファクト以外のビルドツールを使う場合にもこの問題は出てくると思うので、その際の検討の参考になればと思います。
これからやりたいこと
ライブラリのバージョンを上げるのって手間ですよね。
自動でバージョンを上げてくれるサービスがあったりするので、これを利用して自動で更新できるようにしたい。
この記事で書いたように自動生成された定義とそうでない定義は分離できているので、どのファイルをサービスにinputすればいいかは整理できている。
あとはリグレッションテストを完備すればデグレには気付ける、はず!