Skip to content

feat(build): build package images for amd64 and arm64 - #4498

Merged
Aleksei Sviridkin (lexfrei) merged 2 commits into
mainfrom
feat/build-multi-arch
Sep 28, 2026
Merged

Aleksei Sviridkin (lexfrei) merged 2 commits into
mainfrom
feat/build-multi-arch

Conversation

@lexfrei

@lexfrei Aleksei Sviridkin (lexfrei) commented Sep 26, 2026 •

Copy link
Copy Markdown
Contributor

What this PR does

Package images can now be built for amd64 and arm64 in one make image, and a published build is multi-arch unless PLATFORM says otherwise. This is the build half of arm64 support from #1961 and #903. CI still publishes amd64 only until it gets a native arm64 leg, which comes in a separate PR.

Builder stages now run on the build platform and compile for the target one, so Go never runs under emulation. Under emulation it is slow and it crashes: the migration-controller Dockerfile already records a "bad pointer in Go heap" from exactly that. On top of this, PLATFORM defaults to linux/amd64,linux/arm64. A build on an arm64 workstation can't publish an arm64-only digest any more. That happened once already, the cilium pin was arm64-only until 3f36a1b rebuilt it by hand.

Two images were only right because they were emulated. On a native builder they would ship the wrong architecture. token-proxy ran go build with no GOARCH, and kube-ovn calls upstream make build-go, which hardcodes GOARCH=amd64. Both are fixed now. The new bats test wants every compiling stage on $BUILDPLATFORM and actually using TARGETARCH. A bare ARG TARGETARCH does not count, this is how token-proxy got through.

Some things stay amd64. talos and testing pin it with override, because matchbox serves amd64 Talos assets and the sandbox runs qemu-system-x86_64. LOAD=1 builds for the host only, since the classic docker image store can't load an index. Every CI build job pins PLATFORM: linux/amd64, so CI output does not change.

This changes local builds. A make image on the default docker driver with the classic image store now fails with "Multi-platform build is not supported for the docker driver", where before it built for the host. Use a docker-container builder (BUILDER=<name>), or pass PLATFORM=linux/amd64 (or LOAD=1) to build one architecture.

I checked it on an arm64 host with a docker-container builder. cozystack-api, dashboard, kubeovn, platform-migrations, cilium and capi-providers-cpprovider build and push as amd64+arm64 indexes. Binaries pulled from each platform are x86-64 and aarch64: cozystack-api, token-proxy, the kamaji provider manager, cilium-agent and all four kube-ovn binaries. piraeus-server builds for both platforms, but the push to my test registry failed on registry 500s. The capi-providers-cpprovider inline cache works with a multi-platform push. Not covered is running the arm64 images on an arm64 cluster, CI has none.

This is stacked on #4485 and targets its branch, so the diff here is only the two commits on top.

Screenshots

No UI changes.

Downstream repositories

Release note

feat(build): package images build for linux/amd64 and linux/arm64 by default, with Go builders cross-compiling on the build platform; a local `make image` now needs a docker-container builder, or PLATFORM=linux/amd64 / LOAD=1 for a single architecture. CI still publishes amd64.

Summary by CodeRabbit

  • Build Improvements
    • Container image builds now target both amd64 and arm64 by default when building without loading images locally.
    • CI and release builds are pinned to amd64; Talos and testing images remain amd64-only.
    • Cross-platform builds now compile on the builder’s native platform while targeting the requested architecture.
  • Documentation
    • Clarified platform defaults and architecture requirements for local and CI builds.

@coderabbitai

coderabbitai Bot commented Sep 26, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

⚙️ Run configuration

Configuration used: Repository: cozystack/cozystack/.coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: 870e0ab4-8b79-4991-850d-da9407f020db

📥 Commits

Reviewing files that changed from the base of the PR and between 2e8219f and 084ac47.

📒 Files selected for processing (37)
  • .github/workflows/build-main.yaml
  • .github/workflows/build-release.yaml
  • .github/workflows/pull-requests.yaml
  • .github/workflows/tags.yaml
  • docs/agents/overview.md
  • hack/cni-plugins-staging-contract.bats
  • hack/common-envs.mk
  • hack/common-envs_test.bats
  • hack/image-builders-cross-compile.bats
  • hack/image-targets-pass-buildx-args.bats
  • packages/apps/kubernetes/images/cluster-autoscaler/Dockerfile
  • packages/apps/kubernetes/images/kubevirt-cloud-provider/Dockerfile
  • packages/apps/kubernetes/images/kubevirt-csi-driver/Dockerfile
  • packages/apps/kubernetes/images/talos-csr-signer/Dockerfile
  • packages/core/installer/images/cozystack-operator/Dockerfile
  • packages/core/talos/Makefile
  • packages/core/testing/Makefile
  • packages/system/backup-controller/images/backup-controller/Dockerfile
  • packages/system/backupstrategy-controller/images/backupstrategy-controller/Dockerfile
  • packages/system/bucket/images/s3manager/Dockerfile
  • packages/system/capi-providers-cpprovider/images/cluster-api-control-plane-provider-kamaji/Dockerfile
  • packages/system/cozystack-api/images/cozystack-api/Dockerfile
  • packages/system/cozystack-controller/images/cozystack-controller/Dockerfile
  • packages/system/dashboard/images/console/Containerfile
  • packages/system/dashboard/images/token-proxy/Dockerfile
  • packages/system/flux-plunger/images/flux-plunger/Dockerfile
  • packages/system/flux-shard-operator/images/flux-shard-operator/Dockerfile
  • packages/system/kamaji/images/kamaji/Dockerfile
  • packages/system/kubeovn-plunger/images/kubeovn-plunger/Dockerfile
  • packages/system/kubeovn-webhook/images/kubeovn-webhook/Dockerfile
  • packages/system/kubeovn/images/kubeovn/Dockerfile
  • packages/system/lineage-controller-webhook/images/lineage-controller-webhook/Dockerfile
  • packages/system/linstor/images/linstor-csi/Dockerfile
  • packages/system/linstor/images/piraeus-server/Dockerfile
  • packages/system/opensearch-operator/images/opensearch-operator/Dockerfile
  • packages/system/redis-operator/images/redis-operator/Dockerfile
  • packages/system/securitygroup-controller/images/securitygroup-controller/Dockerfile
 __________________________________________________
< Your concurrency story is mostly a horror story. >
 --------------------------------------------------
  \
   \   (\__/)
       (•ㅅ•)
       /   づ
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added area/build Issues or PRs related to image build infrastructure, multi-arch support kind/feature Categorizes issue or PR as related to a new feature size/L This PR changes 100-499 lines, ignoring generated files labels Sep 26, 2026
The remaining Go builder stages, plus the kube-ovn, console and
piraeus-server builders, ran on the target platform. A multi-platform
build therefore ran the whole compiler under QEMU: slow, and for Go
unreliable, as the migration-controller Dockerfile already records a
"found bad pointer in Go heap" crash from exactly that. The builders
now run on $BUILDPLATFORM and produce the target architecture from
TARGETARCH.

Two builders only produced the right architecture because they were
emulated. token-proxy ran `go build` with no GOOS/GOARCH, and kube-ovn
calls upstream `make build-go`, which hardcodes GOARCH=amd64 even on an
arm64 build. On a native builder both would have shipped host binaries
inside the other architecture's image, and nothing fails until a node
of that architecture starts the container. token-proxy now passes the
target explicitly; kube-ovn picks build-go or its arm64 twin
build-go-arm from TARGETARCH.

The console builder emits static JS and linstor-server's debian/control
declares every package Architecture: all, so both run natively without
a target.

A bats test requires every stage that compiles to sit on a BUILDPLATFORM
FROM and to read TARGETARCH or TARGETPLATFORM on a RUN or ENV line. A
bare ARG declaration does not count, which is what let token-proxy
through.

Assisted-by: LLM
Signed-off-by: Aleksei Sviridkin <[email protected]>
PLATFORM defaulted to empty, so buildx built for the host alone. On an
arm64 workstation `make image` published an arm64-only digest, which
amd64 nodes then fail to pull; the cilium pin shipped exactly that
until it was rebuilt by hand. A build now targets linux/amd64 and
linux/arm64 unless PLATFORM is set, and the digest a package stamps is
the index digest.

LOAD=1 keeps building for the host only. The classic docker image store
cannot load a multi-platform index, and loading is the documented local
workflow.

This changes local pushes: a `make image` on the default docker driver
without the containerd image store now fails with "Multi-platform build
is not supported for the docker driver", where it used to build for
the host. Such a build needs a docker-container builder (BUILDER), or
LOAD=1 or an explicit PLATFORM for a single architecture.

talos and testing pin linux/amd64 with `override`, placed before the
include because common-envs.mk expands BUILDX_ARGS immediately. The
Talos assets are amd64: matchbox bakes the kernel and initramfs into
its image, and the e2e sandbox boots the disk under qemu-system-x86_64.
An arm64 variant of either would still serve or boot amd64 files.

CI pins linux/amd64 on every image build. Nothing there sets up QEMU,
fork builds export a single-platform archive, and tags.yaml builds on
the default docker driver, so the default would break or emulate those
jobs. Native arm64 builds belong in a leg of their own.

Assisted-by: LLM
Signed-off-by: Aleksei Sviridkin <[email protected]>
IvanHunters
IvanHunters previously approved these changes Sep 28, 2026

@IvanHunters IvanHunters left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed statically in a hermetic clone. Ran 20 bats tests, mutation-tested both new checks, and verified by enumeration that every CI build job stays pinned to linux/amd64. LGTM. Note: the arm64 build path is not yet exercised by CI, as the PR discloses.

Base automatically changed from fix/build-shared-buildx-args to main September 28, 2026 16:36
Aleksei Sviridkin (lexfrei) added a commit that referenced this pull request Sep 28, 2026
## What this PR does

The VPN app can't run on arm64 nodes. Upstream publishes
`quay.io/outline/shadowbox` for amd64 only, so on arm64 the pod
crash-loops with `exec /usr/local/bin/docker-entrypoint.sh: exec format
error`. The last upstream release is v1.12.3 from February 2025. The ARM
request there, OutlineFoundation/outline-server#1009, is still open.

Upstream's own build already knows how to target arm64, it just never
publishes that build. It pins an arm64 node base, cross-compiles
outline-ss-server and has prometheus checksums for both architectures.
This PR rebuilds v1.12.3 from source as a multi-arch image, following
the layout of upstream's `docker:build` task. The source is pinned to
the release commit. The webpack bundle is built once on the build
platform, the Go server and prometheus per target. The runtime base is
the node 18.18.0 index whose amd64 and arm64 manifests are exactly the
ones upstream pins, so amd64 keeps the same base layers.

The chart now reads the image the same way as the other first-party
images, from a stamped `.tag` file. It is pinned by digest instead of
the floating `:stable` tag. Until the first CI build stamps the file, it
holds upstream v1.12.3 by digest, which is what `:stable` resolves to
today. The package is in the root `build:` list, so every build path
refreshes it.

I built both platforms and checked each one. The binaries are the right
ELF architecture, the management API answers and access keys get
created. On an arm64 test cluster the VPN app with this image went
Ready. An HTTPS request through a real shadowsocks client on another
node came back 200. The helm-unittest no longer asserts the image
string, since that would only restate the pin.

This gives an arm64 variant only when the build gets a platform list.
Current CI builds amd64 only, so until the multi-arch build lands
(#4498) this is a same-behaviour rebuild. Also, `cozystack/shadowbox` is
a new image. Its e2e registry repo may come up private on first push,
which shows up as a 403 on pull, not as a code problem.

### Screenshots

No UI changes.

### Downstream repositories

- [x] No downstream repository is affected by this change
- [ ] [cozystack/website](https://github.com/cozystack/website) -
follow-up:
- [ ]
[cozystack/terraform-provider-cozystack](https://github.com/cozystack/terraform-provider-cozystack)
- follow-up:
- [ ]
[cozystack/ansible-cozystack](https://github.com/cozystack/ansible-cozystack)
- follow-up:
- [ ] [cozystack/ccp](https://github.com/cozystack/ccp) - follow-up:
- [ ] [cozystack/talm](https://github.com/cozystack/talm) - follow-up:
- [ ] [cozystack/cozyhr](https://github.com/cozystack/cozyhr) -
follow-up:
- [ ] [cozystack/cozy-proxy](https://github.com/cozystack/cozy-proxy) -
follow-up:
- [ ]
[cozystack/cozystack-telemetry-server](https://github.com/cozystack/cozystack-telemetry-server)
- follow-up:
- [ ]
[cozystack/external-apps-example](https://github.com/cozystack/external-apps-example)
- follow-up:
- [ ] [cozystack/examples](https://github.com/cozystack/examples) -
follow-up:
- [ ] [cozystack/community](https://github.com/cozystack/community) -
follow-up:

The diff touches one app's chart and Makefile and the root `build:`
list. No entry in the trigger map covers an app's image source.

### Release note

```release-note
feat(vpn): the VPN app now uses a shadowbox image that Cozystack builds from the upstream v1.12.3 source, pinned by digest, instead of the floating amd64-only `:stable` upstream tag. The image builds for amd64 and arm64; releases publish the arm64 variant once the multi-arch release build is in place.
```


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Updates**
* The VPN application now uses a pinned Shadowbox image instead of the
stable image tag.
  * The VPN image build supports multiple architectures.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
@lexfrei
Aleksei Sviridkin (lexfrei) merged commit 6d759cc into main Sep 28, 2026
50 of 51 checks passed
@lexfrei
Aleksei Sviridkin (lexfrei) deleted the feat/build-multi-arch branch September 28, 2026 16:43
Aleksei Sviridkin (lexfrei) added a commit that referenced this pull request Sep 28, 2026
…unlisted images for arm64 (#4552)

## What this PR does

This brings together four approved PRs that give the remaining
first-party images an arm64 build. Each of them lost its approval when
#4498 merged and GitHub retargeted it to main, so I merged them into one
branch to review and approve once. Each original PR has the full
description and review history, and I'll close them in favour of this
one.

- #4507 builds the Talos installer and matchbox for arm64. Releases also
publish the arm64 ISO, disk images, kernel and initramfs.
- #4515 builds the KubeVirt Velero plugin in-tree for amd64 and arm64.
- #4525 adds flux-plunger, keycloak-operator, kilo and
migration-controller to the root `build:` list, so CI rebuilds them like
every other image. A test keeps any new image target from being left
out.
- #4528 rebuilds the Harbor v2.15.1 images for amd64 and arm64, because
upstream publishes v2.15 for amd64 only.

Release builds still run with `PLATFORM=linux/amd64`, except the Talos
installer, so these images ship arm64 only once #4505 adds the arm64
leg. This PR makes that leg buildable.

A few commits are new on top of the originals. The velero test no longer
pins the plugin's image string, because the release build stamps its own
rebuild over it. It now checks the plugin init container by repository
name. A promote-retag test comment now says what it guards, and the
image-refs doc no longer calls its fixed gaps open. The merge with main
keeps the Harbor portal in the list of stages whose output has no
architecture.

### Screenshots

Not a UI change.

### Downstream repositories

I left every box empty and opened nothing in other repositories. Two
repositories are touched, and neither follow-up is needed before this
merges:

- cozystack/website names the amd64 assets in
`content/en/docs/next/install/talos/iso.md` and
`install/providers/oracle-cloud.md`. The install pages could offer the
arm64 downloads once a release carries them. No asset is renamed, so the
current links keep working.
- cozystack/talm points at `ghcr.io/cozystack/cozystack/talos:<version>`
in `charts/cozystack/values.yaml`. Once a release publishes that tag as
a two-arch index, the same reference works on arm64 nodes, so nothing
there has to change.

- [ ] No downstream repository is affected by this change
- [ ] [cozystack/website](https://github.com/cozystack/website) -
follow-up:
- [ ]
[cozystack/terraform-provider-cozystack](https://github.com/cozystack/terraform-provider-cozystack)
- follow-up:
- [ ]
[cozystack/ansible-cozystack](https://github.com/cozystack/ansible-cozystack)
- follow-up:
- [ ] [cozystack/ccp](https://github.com/cozystack/ccp) - follow-up:
- [ ] [cozystack/talm](https://github.com/cozystack/talm) - follow-up:
- [ ] [cozystack/cozyhr](https://github.com/cozystack/cozyhr) -
follow-up:
- [ ] [cozystack/cozy-proxy](https://github.com/cozystack/cozy-proxy) -
follow-up:
- [ ]
[cozystack/cozystack-telemetry-server](https://github.com/cozystack/cozystack-telemetry-server)
- follow-up:
- [ ]
[cozystack/external-apps-example](https://github.com/cozystack/external-apps-example)
- follow-up:
- [ ] [cozystack/examples](https://github.com/cozystack/examples) -
follow-up:
- [ ] [cozystack/community](https://github.com/cozystack/community) -
follow-up:

### Release note

```release-note
feat(talos): the Talos installer image is built for amd64 and arm64, with arm64 profiles that carry drbd and zfs, and the bootbox matchbox image network-boots arm64 machines as well as amd64 ones. Releases publish the arm64 Talos ISO, disk images, kernel and initramfs.
feat(velero): the KubeVirt Velero plugin is now built in-tree for amd64 and arm64, so the velero server starts on arm64 clusters.
build: flux-plunger, keycloak-operator, kilo and migration-controller are now rebuilt by CI like every other first-party image, so their pinned images pick up source patches and base-image updates and are published for amd64 and arm64.
feat(harbor): the Harbor application now runs on arm64 nodes. Its component images are rebuilt from the pinned upstream v2.15.1 release for amd64 and arm64, because upstream publishes v2.15 images for amd64 only.
```


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Talos images and boot assets now support both AMD64 and ARM64,
including architecture-specific installation and cloud images.
* Added build support for Harbor components and the KubeVirt Velero
plugin.
  * Nightly builds now include ARM64 release assets.
* **Improvements**
* Harbor uses pinned chart and image versions, with built image
references recorded automatically.
* Added checks to catch image packages missing from the main build and
to verify multi-architecture installer output.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Aleksei Sviridkin (lexfrei) added a commit that referenced this pull request Sep 29, 2026
…leases on it (#4505)

## What this PR does

Pre-release builds now publish every image as an amd64 and arm64 index,
and a release fails if anything it ships is single-arch. Main, release
lines and pull requests keep building amd64 only. It builds on #4498,
which made every image buildable for both architectures. Part of #1961.

**This PR is on hold until everything under "Merge after" has landed.**
Merged earlier, the new gate fails the next rc on the images those PRs
fix.

### How a pre-release is built

A pre-release tag gets a second job, for arm64, next to the existing
release job. That covers `-rc.N`, `-beta.N` and `-alpha.N` tags. It runs
on the CNCF arm64 pool (`oracle-vm-24cpu-96gb-arm64`), builds the same
images with `PLATFORM=linux/arm64`, and pushes them as `<tag>-arm64`.
`prepare-release` waits for it, and if the arm64 job fails the rc fails
with it. There is no amd64-only fallback. Stable tags do not run it:
promotion copies the rc digests with `skopeo copy --multi-arch all`, so
a stable release inherits the indexes.

After the amd64 build, the stitch joins each amd64 image with its arm64
twin into one index. It lives in `hack/stitch-multiarch.sh`. Every tag
of the amd64 image moves to the index, the component-versioned ones
included. The script then rewrites the digests in the tree and
republishes the packages artifact and the installer chart pinned on
them. It reads refs through `hack/lib/image-refs.sh`, and it fails if an
old digest survives anywhere outside `charts/`.

### The gate

A new check fails the rc when a pinned digest in the tree is not an
index with both linux/amd64 and linux/arm64. It lives in
`hack/verify-multiarch.sh`. It checks first-party and third-party refs
alike. Images that are amd64 by nature go into
`hack/multiarch-allowlist`, one repository per line, each with a
mandatory reason. Today that is only the e2e sandbox. An entry that
matches nothing is reported as stale.

The static gate only sees digest-pinned refs in the tree. A tag-only
ref, an image from a vendored chart default, or one an operator starts
at runtime is invisible to it. So the rc e2e also audits every image its
nodes pulled, with the same check and the same allowlist. Neither covers
a package the e2e never installs.

### Nightly

A new nightly workflow builds main for arm64. It keeps the arm64 build
cache warm, since the rc job only reads it. It also prints the
single-arch refs that would block the next rc. Nothing it builds is
published.

### Smaller changes

- `CACHE_TAG` in `hack/common-envs.mk` moves the default cache ref, so
the arm64 build keeps a cache of its own.
- `MATRIX_ARCH=arm64` makes `hack/build-matrix.sh` leave out the
amd64-only e2e sandbox.
- The kamaji provider image is pushed under the build's `IMAGE_TAG` like
every other image. Its Makefile set `IMAGE_TAG` itself, so concurrent
builds overwrote each other's component tag. Closes #4503.
- `hack/nightly-mirror.sh` verifies a copy against the raw manifest
digest. Plain `skopeo inspect` resolves an index to one platform's
child.
- matchbox is rebuilt for both platforms in the amd64 release job. The
arm64 leg skips the talos package, so there is no arm64 half to stitch,
and its Dockerfile only copies files, so no QEMU is needed. Releases
then network-boot arm64 machines too. Closes #4524.

The stitch also rewrites the kamaji ref inside `files/components.gz`,
recompressed with `gzip -n` so the bytes are reproducible, so the kamaji
control-plane provider ships multi-arch too. keda and kuberture now name
the repository next to their pinned digest, so the gate can resolve
them; their rendered manifests do not change.

### Merge after

- #4552 builds the Talos installer, matchbox, Harbor and the Velero
KubeVirt plugin for arm64, and adds flux-plunger, keycloak-operator,
kilo and migration-controller to the root `build:` list. It replaces
#4507, #4515, #4525 and #4528.
- #4549 moves ingress-nginx, cozy-proxy, cozystack-scheduler and
keycloak-kms-proxy to their multi-arch releases, which are already
published.

The rest of the stack is merged: #4485, #4498, #4512, #4517, #4521 and
#4522 here, and the multi-arch PRs in
ingress-nginx-with-protobuf-exporter, cozy-proxy, cozystack-scheduler
and keycloak-kms-proxy.

### Verification

The bats suites pass: stitch, verify-multiarch, build-matrix,
common-envs, nightly-mirror and the release contracts. The first three
also pass under `hack/cozytest.sh` with dash. Each new test was red
before its implementation, and the verify-multiarch and stitch tests
each have a mutation that turns them red. I ran `verify-multiarch
--report` against main. It lists the first-party refs the stitch will
fix, plus the third-party and special cases above. The workflows have
not run yet. The arm64 job's tools and duration, the stitch against real
registries, and the audit are checked for the first time on the nightly
and on the next rc.

### Screenshots

Not a UI change.

### Downstream repositories

- [x] No downstream repository is affected by this change
- [ ] [cozystack/website](https://github.com/cozystack/website) -
follow-up:
- [ ]
[cozystack/terraform-provider-cozystack](https://github.com/cozystack/terraform-provider-cozystack)
- follow-up:
- [ ]
[cozystack/ansible-cozystack](https://github.com/cozystack/ansible-cozystack)
- follow-up:
- [ ] [cozystack/ccp](https://github.com/cozystack/ccp) - follow-up:
- [ ] [cozystack/talm](https://github.com/cozystack/talm) - follow-up:
- [ ] [cozystack/cozyhr](https://github.com/cozystack/cozyhr) -
follow-up:
- [ ] [cozystack/cozy-proxy](https://github.com/cozystack/cozy-proxy) -
follow-up:
- [ ]
[cozystack/cozystack-telemetry-server](https://github.com/cozystack/cozystack-telemetry-server)
- follow-up:
- [ ]
[cozystack/external-apps-example](https://github.com/cozystack/external-apps-example)
- follow-up:
- [ ] [cozystack/examples](https://github.com/cozystack/examples) -
follow-up:
- [ ] [cozystack/community](https://github.com/cozystack/community) -
follow-up:

Nothing under `hack/` is moved or renamed, and no make target changes
its default behaviour: `CACHE_TAG` and `MATRIX_ARCH` are opt-in. The
satellite repositories listed under "Merge after" are prerequisites, not
follow-ups this change forces on them.

### Release note

```release-note
ci(release): pre-release builds publish every image as an amd64 and arm64 multi-arch index, and a release fails if any image it ships or pulls in e2e lacks either architecture. Stable releases inherit the indexes through promotion.
```


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Release candidates now build amd64 and arm64 images in parallel and
combine eligible images into verified multi-architecture indexes before
publication.
* A nightly arm64 image build is available for testing and supplies a
build cache for release-candidate builds.
* **Bug Fixes**
* End-to-end checks audit whether pulled images support both
architectures. Audit failures block prerelease checks, while stable
releases continue with a warning.
* End-to-end test artifacts now include collected image references and
multi-architecture audit results.
* **Documentation**
* Updated release and image guidance covers multi-architecture builds,
verification checks, and troubleshooting, including arm64 build and
stitching failures.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/build Issues or PRs related to image build infrastructure, multi-arch support kind/feature Categorizes issue or PR as related to a new feature size/L This PR changes 100-499 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants