Skip to content

fix: produce real multi-arch images from ghcr and ecr workflows#128

Merged
rswanson merged 1 commit intomainfrom
fix/multiarch-docker-builds
May 5, 2026
Merged

fix: produce real multi-arch images from ghcr and ecr workflows#128
rswanson merged 1 commit intomainfrom
fix/multiarch-docker-builds

Conversation

@rswanson
Copy link
Copy Markdown
Member

@rswanson rswanson commented May 5, 2026

Summary

  • ecr-build-and-push.yml had platforms: linux/arm64 hardcoded in both Build and push steps — every image pushed to ECR through this workflow was arm64-only, causing exec format error for consumers running on amd64.
  • ghcr.yml declared a [linux/amd64, linux/arm64] matrix but never passed ${{ matrix.platform }} to docker/build-push-action. Both matrix jobs ran on init4-runners and raced to push identical tags ({{version}}, {{major}}.{{minor}}, latest, sha). No imagetools create step ran, so the published image was a single-arch image, not a manifest list.
  • Both workflows now use the multi-arch pattern already established by release-docker-ghcr.yml: matrix on native runners per arch (ubuntu-latest + ubuntu-24.04-arm), push-by-digest=true,name-canonical=true, digest artifact upload, then a merge job that assembles the manifest list under all metadata-action tags via docker buildx imagetools create.
  • The GHCR attest-build-provenance step moved into the merge job and now attests the merged multi-arch manifest digest, which is what consumers actually pull.

Why amd64 builds were missing

The first failure surfaced via init4tech/builder v1.0.0: pulling the image on amd64 hosts produced exec /usr/local/bin/zenith-builder-example: exec format error. Tracing showed the ECR workflow had no amd64 build step at all, and the GHCR workflow's matrix variable was never wired through to the build action — so both registries published arm64-only images regardless of tag.

Behaviour changes for consumers

  • ECR: builds now run on ubuntu-latest + ubuntu-24.04-arm instead of the init4-runners group. AWS OIDC + ECR login still works the same way and is performed in both the build and merge jobs.
  • GHCR: same runner change. The type=ref,event=pr and per-major/minor semver tags from the original metadata step are preserved.
  • Local /tmp/.buildx-cache was replaced with GHA cache scoped per platform — local cache made no sense across separate runners, and per-platform GHA scope is the same approach used by the original (pre-shared-workflow) ghcr.yml in init4tech/builder.

Test plan

  • Re-tag v1.0.0 (or push a temporary v1.0.1-rc.0) on init4tech/builder and confirm both ECR and GHCR artifacts are real manifest lists with both linux/amd64 and linux/arm64 entries:
    • docker manifest inspect <ecr-repo>/zenith-builder-example:1.0.1-rc.0
    • docker manifest inspect ghcr.io/init4tech/builder:1.0.1-rc.0
  • docker pull --platform=linux/amd64 ... and docker run succeeds on an amd64 host
  • docker pull --platform=linux/arm64 ... and docker run succeeds on an arm64 host
  • Confirm SSH-private-deps path works (init4tech/builder uses requires-private-deps: true)
  • Confirm GHCR build-provenance attestation references the multi-arch manifest digest

🤖 Generated with Claude Code

Both shared workflows produced single-arch images despite advertising
linux/amd64 + linux/arm64 support, causing `exec format error` for
consumers running on the missing architecture.

ecr-build-and-push.yml had `platforms: linux/arm64` hardcoded in both
build steps, so every image pushed was arm64-only regardless of any
intent to support amd64. ghcr.yml declared a platform matrix but never
passed `${{ matrix.platform }}` to docker/build-push-action; both matrix
jobs ran on the same runner group and raced to push identical tags, and
no manifest list was assembled.

Both workflows now follow the same pattern as release-docker-ghcr.yml:
native runners per architecture (ubuntu-latest + ubuntu-24.04-arm),
push-by-digest per platform, then a `merge` job that assembles the
manifest list under the metadata-action tags via
`docker buildx imagetools create`. The ghcr workflow's build-provenance
attestation now attests the merged manifest digest rather than a single
per-arch digest.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@rswanson rswanson merged commit 0f71657 into main May 5, 2026
@rswanson rswanson deleted the fix/multiarch-docker-builds branch May 5, 2026 18:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants