This lesson follows a typical journey to get code into a development or staging environment. You’ll create a docker (OCI) container image, run it locally, and push it to a cloud.
Hinting to the BUILD generator
The example repo contains a power feature of the Aspect CLI. The file .aspect/cli/py3_image.star
is a Starlark extension of the BUILD file generator, which teaches it a simple Regex to perform:
RegexQuery(filter = "__main__.py", expression = """#\\s*oci:\\s*build""")
This means you could place a comment in the app/__main__.py
file, like # oci:build
anywhere in the file to trigger the extension.
Add that line, run bazel configure
again, and you see a py3_image
target named image
is added to the app/BUILD
file.
Run the image
You can build the generated target to produce the OCI image. However most devs expect that a container image is also loaded into the daemon as part of building it, since that’s how docker build
works. To have a similar workflow, the target is also runnable, and calls docker load
for you.
Query again to see what targets are available:
% bazel query --output=label_kind app:all
py_library rule //app:__test__
...
tar rule //app:_image_layers_default
tar rule //app:_image_layers_interpreter
genrule rule //app:_image_layers_manifests
tar rule //app:_image_layers_packages
py_binary rule //app:app_bin
py_venv_rule rule //app:app_bin.venv
py_test rule //app:app_test
py_venv_rule rule //app:app_test.venv
platform_transition_filegroup rule //app:image
oci_load rule //app:image.load
oci_image rule //app:image_image
_jq_rule rule //app:image_image.digest
filegroup rule //app:image_layers
mtree_spec rule //app:image_layers.manifest
oci_load
is the rule kind you want, so run bazel run //app:image.load
The .load
suffix on the label is a convention for a “custom command” in Bazel, it’s similar to typing bazel load //app:image
The command prints the registry and tag that was loaded: Loaded image: localhost/app:latest
That means you can now run it with your container runtime:
% docker run --rm -it localhost/app:latest
_______________________________________
/ \
| -- Built at <unstamped> -- |
| I'm a cow, I found this on the internet |
| <!DOCTYPE html> |
| <html> |
...
Stamping artifacts
Say you deploy the container to prod, and the monitoring system pages you because it’s crash-looping. You want to figure out whether the crashes are on the new version - but you still have <unstamped>
in the program.
When you build artifacts for pushing to an environment, ideally in a continuous delivery step on CI, you are willing to give up some of the determinism guarantee. It’s okay to have some cache misses after you’ve got green test results. Bazel has a --stamp
flag which means that the outputs can intentionally be non-deterministic and use data from VCS.
So run:
% bazel run --stamp app:image.load
% docker run --rm -it localhost/app:latest
_______________________________________
/ \
| -- Built at 1737154064 -- |
| I'm a cow, I found this on the internet |
| <!DOCTYPE html> |
Now you see the header template was re-built with a current timestamp, then the application and the container image updated as well.