Bazel 6.0.0 にアップデートしたときの対応メモ
会社のモノレポで使ってる Bazel を5系から 6.0.0 にあげたときのメモです。
使ってるルールは
- https://github.com/bazelbuild/rules_go
- https://github.com/bazelbuild/bazel-gazelle
- https://github.com/bazelbuild/rules_pkg
- https://github.com/bazelbuild/rules_docker
- https://github.com/bazelbuild/rules_k8s
- https://github.com/matsubara0507/rules_yq
- https://github.com/matsubara0507/rules_elm
- https://github.com/bazelbuild/rules_nodejs (Elm でテストする用)
- https://github.com/matsubara0507/rules_openapi
- https://github.com/bazelbuild/rules_foreign_cc
このうち、いくつかは対応が必要だった(あるいは最新にアップデートする必要があった)ので、それについてメモしておきます。
rules_k8s
rules_k8s は kubectl
を bazel run
で行えるようにするルールです。k8s_object
を使うことで bazel run
で kubectl apply
ができるようになります。
こういう感じのエラーメッセージが出ました:
$ bazelisk test //path/to/subproject/...
ERROR: /sandbox/external/bazel_tools/platforms/BUILD:19:6: in alias rule @bazel_tools//platforms:x86_64: Constraints from @bazel_tools//platforms have been removed. Please use constraints from @platforms repository embedded in Bazel, or preferably declare dependency on https://github.com/bazelbuild/platforms. See https://github.com/bazelbuild/bazel/issues/8622 for details.
ERROR: /sandbox/external/bazel_tools/platforms/BUILD:19:6: Analysis of target '@bazel_tools//platforms:x86_64' failed
ERROR: /path/to/project/oapi-codegen/cmd/oapi-codegen/BUILD.bazel:15:10: While resolving toolchains for target //oapi-codegen/cmd/oapi-codegen:oapi-codegen: invalid registered toolchain '@io_bazel_rules_k8s//toolchains/kubectl:kubectl_windows_toolchain':
...
Bazel では基本的に、依存する外部ツールをホストの OS や CPU に合わせてインストールし直します(設定によってはホストにインストールされたものを使うことも可能です)。この時には、ツールチェインという仕組みを使うのが一般的です。ツールチェインを使うときに OS や CPU を判別するために @platforms//os:linux
みたいのを使うのですが、昔は @bazel_tools/platforms
ってのを使ってたみたいです(たぶん)。かなり長いこと移行期間が設けられており、その間は --incompatible_use_platforms_repo_for_constraints
オプションを使うことで @bazel_tools/platforms
が使えなくなります。Bazel 6.0.0 からはこのオプションがデフォルトでオンになりました。
rules_k8s は、v0.6 まで @bazel_tools/platforms
を使っていたため Bazel 6.0.0 からエラーが出るようになったわけです。--incompatible_use_platforms_repo_for_constraints=false
オプションを追加しても良いですが、v0.7 で修正されているので今回はアップデートして対応しました:
ただし注意点として、release にある通りに WORKSPACE を記述すると:
http_archive(
name = "io_bazel_rules_k8s",
sha256 = "ce5b9bc0926681e2e7f2147b49096f143e6cbc783e71bc1d4f36ca76b00e6f4a",
strip_prefix = "rules_k8s-0.7",
urls = ["https://github.com/bazelbuild/rules_k8s/archive/refs/tags/v0.7.tar.gz"],
)
load("@io_bazel_rules_k8s//k8s:k8s.bzl", "k8s_repositories")
k8s_repositories()
load("@io_bazel_rules_k8s//k8s:k8s_go_deps.bzl", k8s_go_deps = "deps")
k8s_go_deps()
urls
のが 500 ですというエラーが返ってきました(一時的なものかも?)。仕方ないので、git_repository
を使うことにしました:
git_repository(
name = "io_bazel_rules_k8s",
remote = "https://github.com/bazelbuild/rules_k8s.git",
commit = "fee80eb69e1921c076167ebebcf5eea3d2e9c707", # v0.7
shallow_since = "1655492445 -0700",
)
また、rules_go を使っていると呼び出し順によっては次のようなエラーメッセージが出ます:
ERROR: Traceback (most recent call last):
File "/path/to/project/WORKSPACE", line 270, column 12, in <toplevel>
k8s_go_deps()
File "/sandbox/external/io_bazel_rules_k8s/k8s/k8s_go_deps.bzl", line 36, column 31, in deps
go_register_toolchains(go_version)
File "/sandbox/external/io_bazel_rules_go/go/private/sdk.bzl", line 557, column 13, in go_register_toolchains
fail("go_register_toolchains: version set after go sdk rule declared ({})".format(", ".join([r["name"] for r in sdk_rules])))
Error in fail: go_register_toolchains: version set after go sdk rule declared (go_sdk, go_sdk_old)
...
k8s_go_deps
は v0.7 から、go_register_toolchains
を使ってインストールする Go のバージョンを指定できるようになりました。指定しない場合も、何かしらのバージョンをデフォルトとして設定します。go_register_toolchains
はバージョンを指定する場合、既にインストール済みの場合はエラーが返るようになってます。WORKSPACE
ファイルで先に go_register_toolchains
を実行していたためエラーになったというわけです。
なので実行順を変えるか、k8s_go_deps(go_version = "")
とすることで go_register_toolchains
の実行をスキップできるので、エラーを回避できます。
rules_pkg
rules_pkg は rules_docker でイメージを構築する時に配置する実行ファイルをパッケージ化したりするのに使っています:
go_binary(
name = "cli",
...
)
pkg_tar(
name = "bin",
srcs = ["//path/to/subproject:cli"],
mode = "0755",
package_dir = "/usr/local/bin",
)
container_image(
name = "image",
base = "@com_google_distroless_base_debian11//image",
entrypoint = ["/usr/local/bin/cli"],
tars = [":bin"],
)
Bazel 6.0.0 にしたら次のようなエラーが出るようになりました:
$ bazelisk build //path/to/subproject:image
INFO: Build option --platforms has changed, discarding analysis cache.
INFO: Analyzed target //path/to/subproject:image (619 packages loaded, 14433 targets configured).
INFO: Found 1 target...
ERROR: /path/to/subproject/BUILD.bazel:43:8: PackageTar subproject/bin.tar failed: (Exit 127): build_tar failed: error executing command (from target //path/to/subproject:bin) bazel-out/darwin-py2-opt-exec-2B5CBBC6-ST-5f994f96b8a3/bin/external/rules_pkg/build_tar @bazel-out/darwin-fastbuild-ST-5f994f96b8a3/bin/path/to/subproject/bin.args
Use --sandbox_debug to see verbose messages from the sandbox and retain the sandbox build root for debugging
env: python2: No such file or directory
Target //path/to/subproject:image failed to build
Use --verbose_failures to see the command lines of failed build steps.
INFO: Elapsed time: 124.863s, Critical Path: 65.73s
INFO: 447 processes: 2 internal, 445 darwin-sandbox.
FAILED: Build did NOT complete successfully
Python2 を使おうとするがないっぽくてエラーが出てそうです。
解決方法としては簡単で、rules_docker のインストールの前に rules_pkg のインストールを記述するだけです。なぜ Bazel を上げるだけでエラーが起きるようになったかというと、恐らくですが、Bazel 6.0.0 からビルトインであった pkg_tar
が撤去された(rules_pkg を使いましょうとのこと)ようで、そのせいで暗黙で解決されていた依存関係が解決されなくなったのが原因っぽいです(参考)。つまり、元から rules_pkg を rules_docker より先にインストールすべきだが、ビルトインの pkg_tar
のおかげでそうなっていたと。
インストール順を入れ替えたところ、代わりに次のようなエラーが出るようになりました:
ERROR: /sandbox/external/bazel_tools/platforms/BUILD:89:6: in alias rule @bazel_tools//platforms:windows: Constraints from @bazel_tools//platforms have been removed. Please use constraints from @platforms repository embedded in Bazel, or preferably declare dependency on https://github.com/bazelbuild/platforms. See https://github.com/bazelbuild/bazel/issues/8622 for details.
ERROR: /sandbox/external/bazel_tools/platforms/BUILD:89:6: Analysis of target '@bazel_tools//platforms:windows' failed
ERROR: /path/to/project/subproject/BUILD.bazel:53:16: While resolving toolchains for target //subproject:image: invalid registered toolchain '@bazel_skylib//toolchains/unittest:cmd_toolchain':
...
rules_k8s と同じ @bazel_tools//platforms
を使っているせいですね。@bazel_tools
を使っているのは rules_pkg が依存している bazel_skylib の方で、そっちはかなり前に修正されてそうです:
rules_pkg 側がそのアップデートを取り込んだのが比較的最近っぽく:
なので、最新にアップデートすれば修正されました。
container_run_and_extract
と pkg_tar
おまけ:CI/CD 用に、Ruby の入った Docker イメージを Bazel で次のような感じで作っていました:
download_pkgs(
name = "build_ruby_package",
image_tar = "@ubuntu_bionic//image",
packages = [
"git",
"curl",
"libssl-dev",
"libreadline-dev",
"build-essential",
"zlib1g-dev",
"ca-certificates",
],
)
install_pkgs(
name = "image_build_ruby",
image_tar = "@ubuntu_bionic//image",
installables_tar = ":build_ruby_package.tar",
installation_cleanup_commands = "rm -rf /var/lib/apt/lists/*",
output_image_name = "image_build_ruby",
)
# RUBY_VERSION と BUNDLER_VERSION は適当に置き換えてください
container_run_and_extract(
name = "build_ruby",
commands = [
"git clone --depth=1 https://github.com/rbenv/ruby-build.git",
"CONFIGURE_OPTS='--disable-install-doc' ruby-build/bin/ruby-build RUBY_VERSION /root/ruby",
"echo 'gem: --no-rdoc --no-ri' >> /.gemrc",
"export PATH=/root/ruby/bin:$PATH",
"gem install bundler -v BUNDLER_VERSION",
],
extract_file = "/root/ruby",
image = ":image_build_ruby.tar",
)
pkg_tar(
name = "ruby",
srcs = [":build_ruby/root/ruby"],
mode = "0755",
package_dir = "/root",
)
container_image(
name = "image",
base = ":image_base_package",
tars = [
":ruby",
":reviewdog",
# 他に一緒に入れたい実行ファイルなどなど
],
env = {
"PATH": "/root/ruby/bin:$$PATH"
},
)
rules_docker の container_run_and_extract
は Docker コンテナで実行して得た結果のファイルを Bazel の生成物として使える関数です。これを container_image
と組み合わせることで、Dockerfile のマルチステージビルドのようなものを表現できます。Bazel 的にはあんまりお行儀の良い書き方ではないですが、Ruby 処理系はシングルバイナリで配布されてないですし、クロスコンパイルも厳しいので苦肉の策です。
rules_pkg のバージョンを上げたところ、上記の pkg_tar
の部分で次の Issue と同じようなエラーメッセージが出るようになりました:
pkg_tar
がちゃんと TreeArtifact に対応したところ、ただのディレクトリは受け付けないようになったようです。container_run_and_extract
を使っているとはいえ、そもそもディレクトリを取り回すのは Bazel ではアンチパターンっぽく、rules_docker の例やテストを見てもディレクトリを取り回してるものは見当たりませんでした。そこでとりあえず、container_run_and_extract
の最後で tar
をすることにしました:
container_run_and_extract(
name = "build_ruby",
commands = [
"git clone --depth=1 https://github.com/rbenv/ruby-build.git",
"CONFIGURE_OPTS='--disable-install-doc' ruby-build/bin/ruby-build RUBY_VERSION /root/ruby",
"echo 'gem: --no-rdoc --no-ri' >> /.gemrc",
"export PATH=/root/ruby/bin:$PATH",
"gem install bundler -v BUNDLER_VERSION",
"tar -cf /ruby.tar /root/ruby",
],
extract_file = "/ruby.tar",
image = ":image_build_ruby.tar",
)
container_image(
name = "image",
base = ":image_base_package",
tars = [
":build_ruby/ruby.tar",
":reviewdog",
# 他に一緒に入れたい実行ファイルなどなど
],
env = {
"PATH": "/root/ruby/bin:$$PATH"
},
)
おしまい
大体は Bazel ルール間の依存関係の問題でした。Bazel の問題に、いわゆる依存パッケージをいい感じに管理するパッケージマネージャー的なのがないというのがあります。しかし実は、Bazel 6.0.0 からは Bzlmod というパッケージマネージャーっぽいのが導入されています(Bazel 5系から experimental として導入はされていた)。毎回痛い目見るので次はこれを試してみようと思います。
Discussion