Bazel, Container Push & Infrastructure in Builds

On the encoding of artifact stores into build systems

Table Of Contents

Today I Explained

A pattern that I generally discouraged is the encoding of artifact stores within build systems, such as having a container registry hardcoded into the docker push (or equivalent actions) in the build system. This isn’t to say that the container registry can’t be specified within the continuous integration pipeline, but that it should be avoided within the build system.

The problem with having these hardcoded into the build system is you run into cases where the build system must now understand the concept of “continuous integration (CI)”, your workstation, and a release deploy of the service. It is pushing the responsibility of “where to upload artifacts” onto the build system, rather than having the build system be aware of “how to expose artifacts”. The responsibility of uploading the artifacts to an artifact store really lies with the continuous integration (CI) pipeline.

This becomes an issue as well for workstations, as they don’t need to use the same artifact stores as the release process. A workstation development environment may not even need to publish container images or library packages to a storage repository. If this is the case, then the actions for publishing artifacts won’t be exercised as part of the development workflow, and solely rely on the continuous integration (CI) pipeline to attest that this publishing system is working as desired.

From the local workstation it may not even be possible to acquire the permissions necessary to locally execute the publishing, as this capability may be limited to the continuous integration pipeline.

An alternative that I often recommend is creating a common approach for uploading artifacts into artifact stores that consumes a standard output from the build system. Instead of relying on the build system to understand how to publish to a given artifact store, it can be the responsibility of the build system to expose its artifacts in this common way.

As an example: bazel run //:artifacts which emits the following: /path/to/artifacts. The resultant location can then be leveraged by a script like:

location="$(bazel run //:artifacts)"

for image in "$location"/images/* ;
do
    docker load "$image"
    target=".."
    docker tag $label $target

    # Sign the image
    # Create common manifests
    # etc, etc
done

A nice advantage of this is that if it is appropriate/necessary for the build system to know how to publish artifacts, it can simply consume this common way by bazel run //:publish -- /path/to/artifacts.