I'd like to migrate away from --define
flags and to build settings per: https://docs.bazel.build/versions/5.0.0/skylark/config.html
Here's the rule to which I'd like to pass command line values.
- Can this be done in practice when using loaded rules?
- Can one access build setting values in rule fields of .bazel files, or are they only accessible to Starlark configurations?
- Is there a way to effectively "subclass" a loaded rule without having access to a published implementation? If
_impl
were public, it seems like I might be able to wrap it with my own implementation that passes it the flags.
I'm somewhat new to Bazel and still figuring out the right way to conceptualize this stuff. Any guidance is appreciated!
Current Approach
backend/BUILD.bazel:
load("@io_bazel_rules_docker//container:container.bzl", "container_image", "container_push")
# container_image :run_server definition
container_push(
name = "push_server",
format = "Docker",
image = ":run_server",
registry = "gcr.io",
repository = "$(PROJECT_ID)/chat/server",
tag = "$(CONTAINER_TAG)",
)
Then I run:
bazel run \
--platforms=@io_bazel_rules_go//go/toolchain:linux_amd64 \
--define PROJECT_ID=$(gcloud config get-value project) \
--define CONTAINER_TAG=some_feature_branch \
-- //backend:push_server
What I've Tried
A few variations of:
load("//backend:rules.bzl", "gcr_container_push")
load("@bazel_skylib//rules:common_settings.bzl", "string_flag")
load("@io_bazel_rules_docker//container:container.bzl", "container_image")
string_flag(
name = "container_tag",
build_setting_default = "latest",
visibility = ["//visibility:public"],
)
string_flag(
name = "project_id",
build_setting_default = "",
visibility = ["//visibility:public"],
)
# container_image :run_server definition
gcr_container_push(
name = "push_server",
image = ":run_server",
path = "chat/server",
)
backend/rules.bzl:
load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo")
load("@bazel_skylib//lib:paths.bzl", "paths")
load("@io_bazel_rules_docker//container:container.bzl", "container_push")
def _gcr_container_push_impl(ctx):
project_id = ctx.attr._project_id[BuildSettingInfo].value
if len(project_id) == 0:
fail("Please provide a GCP project ID via --//backend:project_id=<PROJECT ID>.")
container_push(
name = ctx.label.name,
format = "Docker",
image = ctx.attr.image,
registry = "gcr.io",
repository = paths.join(project_id, ctx.attr.path),
tag = ctx.attr._container_tag[BuildSettingInfo].value,
)
_gcr_container_push_attrs = {
"image": attr.label(
allow_single_file = [".tar"],
mandatory = True,
doc = "The label of the image to push.",
),
"path": attr.string(
mandatory = True,
doc = "The name of the image within the repository. Ex. gcr.io/project_id/<PATH>:tag.",
),
"_container_tag": attr.label(default = Label("//backend:container_tag")),
"_project_id": attr.label(default = Label("//backend:project_id")),
}
gcr_container_push = rule(
implementation = _gcr_container_push_impl,
attrs = _gcr_container_push_attrs,
executable = True,
)
Then I run:
bazel run \
--platforms=@io_bazel_rules_go//go/toolchain:linux_amd64 \
--//backend:project_id=ggx-prototype \
-- //backend:push_server
Which returns:
Error in container_push_: 'container_push_' can only be called during the loading phase