Add reusable actions for native BuildKit build in GHA#273
Conversation
This PR fundamentally changes how our images are built. The usage of the Builder container is dropped in favor of "native" build using BuildKit with docker/build-push-action. Dockerfiles are now the single source of truth for all labels and build arguments - the build metadata (version, date, architecture, repository) is passed via --build-arg and consumed directly in the Dockerfile's LABEL instruction, removing the need for external label injection. Build caching uses GitHub Actions cache as the primary backend, with inline cache metadata embedded in pushed images as a fallback for cache reuse across git refs (since GHA cache is scoped per branch/tag). Registry images are verified with cosign before being used as cache sources. Images are compressed with zstd (level 9) instead of gzip, reducing image size and improving pull times on registries and runtimes that support it. Multi-arch support is handled by building per-architecture images in parallel on native runners (amd64 on ubuntu-24.04, aarch64 on ubuntu-24.04-arm), then combining them into a single manifest list using docker buildx imagetools. Thanks to the caching, the builder workflow now also runs on push to the master branch, keeping the GHA cache warm for release builds without adding significant CI cost. A reference implementation is in home-assistant/docker-base#347.
The common convention are dashes, stick with that.
If there are no steps following the build that require the image, the load is pointless and may just delay the build with no real benefit.
This PR completely drops usage of the builder action in favor of new actions introduced in home-assistant/builder#273. This results in faster builds with better caching options and simple local builds using Docker BuildKit. The image dependency chain currently still uses per-arch builds but once docker-base and docker repositories start publishing multi-arch images, we can simplify the action a bit further. The idea to use composite actions comes from #162245 and this PR fully predates it. There is minor difference that the files generated twice in per-arch builds are now generated and archived by the init job.
agners
left a comment
There was a problem hiding this comment.
I like the re-use of cosign-verify. Also that we sign the actual image digest now, it's what cosign recommends 👍 Looks quite good to me 💪. Some minor nits/comments.
|
Please take a look at the requested changes, and use the Ready for review button when you are done, thanks 👍 |
actions/build-image/action.yml
Outdated
| build_args+=("BUILD_VERSION=${VERSION}") | ||
| build_args+=("BUILD_ARCH=${ARCH}") | ||
| build_args+=("BUILD_DATE=${build_date}") | ||
| build_args+=("BUILD_REPOSITORY=https://github.com/${GITHUB_REPOSITORY}") |
There was a problem hiding this comment.
After thinking this over, I suggest that we don't set any build args by default and that the user should specify them. This makes it clearer which one will be used.
Also, for example, the input arg version is only used to set a build arg, which I think is better if the caller specifies it directly. If the caller is specifying it, it has the advantage that he can also control the name and does not need to go through the builder to find out which ones are set automatically.
Also, in case of the input arg version, it is required, and so the caller cannot use it without. For example, if he uses it somewhere, where he doesn't want to specify the version (Intermediate version or version just used in build pipelines)
There was a problem hiding this comment.
I'm a bit lost, what exactly you want to remove from defaults? These in the action?
I meant to use sane defaults for all arguments, so it will be easy to reuse the action/workflow it without unnecessary boilerplate. If you want to remove it from the action, I'd at least prefer to keep it in the workflow to make reuse in plugin/apps build easy. And e.g. in the case of BUILD_DATE I think there's no benefit at all in requiring the user to fill it out. The action is meant to abstract common patterns of building our images which share common structure - otherwise we'd make it just a generic "docker/build-push-and-cosign" action which will require boilerplate in every repo.
Edit: Turns out that calling reusable workflow from another repo causes issues with Cosign signing, so this is not the way.
There was a problem hiding this comment.
This has been largely refactored after the discussion in the Core PR. The only build args passed now are BUILD_ARCH and BUILD_VERSION. The first one is used even in some of HA containers to handle different architectures, the other one is rather to keep the contract given previously: https://developers.home-assistant.io/docs/apps/configuration?_highlight=build_version#build-args
The BUILD_FROM will be removed here (the source of it was build.yaml), while Supervisor will likely keep passing it from the build.yaml for a while until we drop the support for it there too.
…ulti-arch-manifest actions
Unlike in reusable workflow, in action it's evaluated with the repository from where the action is called.
With the new actions added in #273, the old action and build container will no longer be maintained. To make the landing softer, especially for those repos that still use the discouraged `@master` action pinning, only print warnings until they migrate or pin the action to the old release. The references to the old action and builder usage are now removed from the README and a high-level description of the new workflow added.
* Deprecate usage of `home-assistant/builder`, update README With the new actions added in #273, the old action and build container will no longer be maintained. To make the landing softer, especially for those repos that still use the discouraged `@master` action pinning, only print warnings until they migrate or pin the action to the old release. The references to the old action and builder usage are now removed from the README and a high-level description of the new workflow added. * Elaborate on legacy action deprecation Co-authored-by: Stefan Agner <stefan@agner.ch> --------- Co-authored-by: Stefan Agner <stefan@agner.ch>
This PR provides a set of reusable composite actions that replace the Builder container with "native" BuildKit builds using docker/build-push-action.
actions/build-imagebuilds and optionally pushes and signs a single-architecture image. Build metadata (BUILD_ARCH,BUILD_VERSION) is passed to the Dockerfile via--build-arg, while OCI and Home Assistant labels (io.hass.arch,io.hass.version,org.opencontainers.image.*) are applied directly by the action through docker/build-push-action's label support. Additional build args and labels can be passed through thebuild-argsandlabelsinputs. Images are compressed with zstd (level 9) instead of gzip, reducing image size and improving pull times on registries and runtimes that support it. Build caching uses GitHub Actions cache as the primary backend, with inline cache metadata embedded in pushed images as a fallback for cache reuse across git refs (since GHA cache is scoped per branch/tag). Pushed images are signed with Cosign, with retry and exponential backoff. Base and cache images can optionally be verified before the build starts.actions/cosign-verifyverifies the Cosign signature of a container image against a certificate identity and OIDC issuer, with retry logic and an optional allow-failure mode.actions/prepare-multi-arch-matrixvalidates the requested architectures (amd64, aarch64) and outputs a JSON matrix mapping each to a native runner (ubuntu-24.04, ubuntu-24.04-arm) and a registry image name, ready to be consumed by a build matrix job.actions/publish-multi-arch-manifestcombines per-architecture images into a single manifest list usingdocker buildx imagetools create, applies all requested tags, and signs the resulting manifest with Cosign.Together, these actions support a workflow where per-architecture images are built in parallel on native runners, then combined into a multi-arch manifest. Thanks to the caching, the build can also run on push to the master branch to keep the GHA cache warm for release builds without adding significant CI cost.
A reference implementation is in home-assistant/docker-base#347.