Skip to content

feat(docker-release): split mode — one native-arch leg per platform, no QEMU - #55

Merged
blairham merged 1 commit into
mainfrom
feat/docker-release-split
Aug 15, 2026
Merged

feat(docker-release): split mode — one native-arch leg per platform, no QEMU#55
blairham merged 1 commit into
mainfrom
feat/docker-release-split

Conversation

@blairham

Copy link
Copy Markdown
Contributor

Why

Multi-arch images on the single-job workflow would emulate arm64 with QEMU — 5–10× on compile-heavy stages (trading's .NET solution being the worst case). GitHub/EKS give us native arm64 runners, so the build should fan out per platform, not per image.

What

docker-release-split.yml — a new reusable workflow (separate file: callers pin @main, and this changes job topology), built on stevedore v0.0.10 split/merge (blairham/stevedore#15):

plan (change detection + version pins, once)
  ├── build linux/amd64  — native runner, all planned images, one BuildKit, push by digest
  ├── build linux/arm64  — native runner, all planned images, one BuildKit, push by digest
  └── merge              — imagetools create manifest lists, release tail, markers, Dispatch

Design points:

  • The trading stevedore cutover: exploit the build-once Dockerfile (compile once, stamp 13 images) #39 build-once property survives, per platform. Each leg builds the whole planned set on one BuildKit, so a shared compile stage (trading#1967) still compiles once per leg. Wall clock ≈ one single-arch build + a cheap merge.
  • No partial images. Legs push untagged, by digest; nothing tagged exists until merge, and stevedore's merge hard-fails if any configured platform lacks a digest.
  • Version safety without locking. Legs push no tags, so the ECR highest+bump resolution merge performs sees what plan saw; pins make it explicit. The shared docker-release concurrency group serializes with the single-job workflow.
  • Per-platform layer cache. cache: gha → one scope per platform (a shared scope would thrash under the 10GB cap now that build stages differ by TARGETARCH); cache: registry<REGISTRY>/<repo>/buildcache:<plat> in ECR for repos whose build stage outgrows GHA cache (trading). Buildcache ECR repos must be created in platform-gitops before enabling.
  • Runners are inputs. amd64-runner/arm64-runner labels, so switching the legs to the EKS/ARC self-hosted runners is caller config, not a workflow change. Note the ubuntu-24.04-arm default is public-repo-only free.
  • No no-push input — a split leg is a push (by digest); validation builds stay on docker-release.yml, as do single-platform repos.

Validation

  • actionlint clean locally (including shellcheck).
  • stevedore v0.0.10 split/merge is released (binary + image workflows green) and its split leg + merge were exercised end-to-end in dry-run against a real config.
  • Per this repo's rules, semantic validation needs a draft PR in a real caller pointing uses: at this branch — suggest k5s first (two platforms, cross-compile Dockerfile already on its feat/dockerfile-cross-compile branch), then trading with cache: registry once its buildcache repo exists.

…no QEMU

docker-release-split.yml is the multi-arch variant of docker-release.yml,
built on stevedore v0.0.10's split/merge (blairham/stevedore#15):

- plan pins the release set and per-image ECR versions once, so both
  legs and the merge decide identically.
- One leg per platform on a runner of that architecture; each leg
  builds ALL planned images on one BuildKit — the #39 build-once
  property survives per platform — and pushes per-arch images untagged,
  by digest, into a shared artifact.
- merge stitches the digests into tagged manifest lists, runs the
  release tail, advances marker refs, and notifies Dispatch. It refuses
  to publish while any platform lacks a digest, so a failed leg can
  never ship a partial image.

Layer cache is per platform: cache=gha scopes as stevedore-<plat>;
cache=registry writes <REGISTRY>/<repo>/buildcache:<plat> (no 10GB cap,
suited to trading's .NET build stage). Private repos must pass
arm64-runner (org larger runners or the EKS/ARC labels) — the default
ubuntu-24.04-arm is free for public repos only.

A separate file rather than an edit in place: callers pin @main, and
split changes job topology; repos opt in by switching uses:.
@blairham
blairham merged commit 9ad324f into main Aug 15, 2026
2 checks passed
@blairham
blairham deleted the feat/docker-release-split branch August 15, 2026 11:44
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.

1 participant