LoginSignup
4

More than 3 years have passed since last update.

Golang+Bazelで依存ライブラリをいい感じに管理する

Posted at

この記事はOpenSaaS Studio Advent Calendar 2019、11日目の記事です。3日目はPython+Bazelでしたが、今回はGolang+Bazel。

はじめに

GolangのプロジェクトでBazelを使う場合、依存ライブラリの二重管理が必要になることがあります。
二重管理は変更の反映漏れにつながったり管理方法が理解しづらくなるため基本的には避けるべきですが、上手くハンドリングできればビルド速度などBazelのメリットを享受しつつ問題なく運用できます。
この記事では、私がジョインしているプロジェクトでこの課題にどう対応したのか紹介します。

依存ライブラリの二重管理

BazelではWORKSPACEというファイルで依存ライブラリを定義します(マクロに定義してWORKSPACEでそのマクロを呼ぶ方法もあります)が、Golangのアプリを開発する場合にはIDEの対応状況やワークフローなどを考慮してgo.modに依存ライブラリを定義することがほとんどだと思います。
その場合、以下のように複数のファイルに同じ依存ライブラリの定義が存在するため二重管理が必要になります。

WORKSPACE
go_repository(
    name = "com_google_cloud_go",
    importpath = "cloud.google.com/go",
    sum = "h1:0sMegbmn/8uTwpNkB0q9cLEpZ2W5a6kl+wtBQgPWBJQ=",
    version = "v0.44.3",
)
go.mod
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.bzlgo_repositoriesという名前で依存ライブラリを定義するためのマクロが生成されます。
生成されたマクロは以下のようにWORKSPACEで呼び出せば依存ライブラリの定義がロードされます。

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コメントで削除されないようにしてください。

WORKSPACE
### 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すればいいかは整理できている。
あとはリグレッションテストを完備すればデグレには気付ける、はず!

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
4