service.yaml beside them since the last step, inert, waiting for something to read them:
cmd/server/service.yaml (abridged)
service.yaml is a service” — and conventions are what nobody wants to hand-write BUILD files for. Only name and port are read by anything; the rest of the file, including a resources: block this snippet leaves out, is there to look like a real deployment descriptor. Gazelle already generates go_library and go_binary by reading Go sources. This step teaches it one more rule.
Run it
The generated targets are already committed at this step, so throw them away first and ask for them back:cmd/server/BUILD.bazel:
port: 8080 became exposed_ports; name: server became the image label and the push repository.
How it works
Opentools/gazelle/service_image.axl. An extension has two halves.
prepare says what to read.
service.yaml cost nothing.
The engine running this is called Orion — that is the name in the last line of the file, and it is why the repository can use a prebuilt Gazelle with no Go toolchain.
declare says what targets belong in one package. Two of the four it adds are plumbing — go_cross_binary and tar exist to get a Linux binary into a layer, and neither comes from the YAML. The two your service.yaml actually shapes are image and push.
service.yaml, it calls ctx.targets.remove(...) instead — which is how a generated target disappears when its YAML does.
After declare there is one gazelle_rule_kind registration per rule this
extension writes. Each names three things:
From— where the rule comes from, a.bzllabel or a bare module name like@tar.bzl. This is what writes theloadstatements you saw at the top of the generated BUILD file.MergeableAttrs— attributes the generator owns, re-asserted on every run.NonEmptyAttrs— the attribute whose absence means the rule is dead. It is what letsctx.targets.remove(...)actually delete a target rather than leave an empty one behind.
How Gazelle finds it
A task under.aspect/ needs no registration. A Gazelle extension does — it is
named by the Gazelle binary that hosts it, which step-6 also added:
tools/gazelle/BUILD.bazel
aspect gazelle drives, and it is the prebuilt one because
only that binary hosts Orion. The comments in the file cover repo_config,
which is required whenever go is in languages.
It is idempotent
exposed_ports follows. That is MergeableAttrs doing its job: the generator owns that attribute, so a re-run re-asserts it.
Then ship it
The generatedoci_push targets are real. aspect delivery runs them:
pushed blob: lines before the table.
Three images, live on ttl.sh — an anonymous registry that needs no account and expires an image an hour after it is pushed.
ttl.sh has one global namespace, so everyone in a workshop pushes to the same
three repositories. The builds are reproducible, so everyone pushes identical
bytes and the tag expires within the hour — but if you would rather have your
own, change
_REGISTRY in the extension to ttl.sh/<something-unique> and
re-run aspect gazelle.service.yaml became BUILD targets, and the BUILD targets became images.
config.axl is what told delivery where to look:
oci_push in this repository was generated from a service.yaml, so that query means “all of them”.
Next: hand all of this to an agent.
