Skip to content

Fix precompiled image publish silently dropping non-native-arch manifests - #873

Open
shivakunv wants to merge 3 commits into
mainfrom
skopeo-multiarch-issue
Open

Fix precompiled image publish silently dropping non-native-arch manifests#873
shivakunv wants to merge 3 commits into
mainfrom
skopeo-multiarch-issue

Conversation

@shivakunv

@shivakunv shivakunv commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

The publish step for precompiled driver images used skopeo copy oci-archive:file.tar docker://... without --all. For a multi-platform (amd64+arm64) OCI archive, skopeo doesn't error on that, it silently collapses to a single current-arch manifest, so the pushed image only had one architecture even though the build produced both.

Fix: add --all to the existing oci-archive: copy. I verified locally against the exact skopeo version CI installs (1.13.3+ds1-2ubuntu0.24.04.3, pulled from a real scheduled run's logs) and against a real push to ghcr.io that --all alone is enough, no need to extract the archive to an oci: directory first like the original fix did.

Also kept and adjusted the post-publish verification, since a skopeo copy that exits 0 isn't proof the full index was published (that's exactly how this bug shipped unnoticed in the first place). It compares the local artifact's manifest against what's actually live in the registry, and now handles both shapes correctly: an index (the multi-arch legs) compares per-platform digests, a plain manifest (ubuntu22.04, ubuntu26.04, and ubuntu24.04 azure-fde, which build amd64-only per multi-arch.mk) compares config/layer digests instead of silently passing on [] == [].

One correction on the linked skopeo issue: that issue is a different, still-open failure (a hard crash on a flat index with no attestation manifests), not the silent-drop behavior described above. Related context, but not the exact bug we're hitting here, that's just the missing --all flag.

@shivakunv shivakunv changed the title Skopeo multiarch issue Skopeo multi-arch issue Jul 20, 2026
@shivakunv
shivakunv force-pushed the skopeo-multiarch-issue branch 4 times, most recently from ffc8d93 to dc84491 Compare July 21, 2026 06:14
@shivakunv
shivakunv marked this pull request as ready for review July 21, 2026 06:15
@shivakunv shivakunv linked an issue Jul 22, 2026 that may be closed by this pull request
…stead of oci-archive

Signed-off-by: Shiva Kumar (SW-CLOUD) <shivaku@nvidia.com>
@shivakunv
shivakunv force-pushed the skopeo-multiarch-issue branch from 47818c3 to df08d06 Compare July 31, 2026 08:06
@rajathagasthya

Copy link
Copy Markdown
Contributor

@shivakunv Can you update the PR title and description to better reflect what you're doing in this PR?

@shivakunv

Copy link
Copy Markdown
Contributor Author

@shivakunv Can you update the PR title and description to better reflect what you're doing in this PR?

updated

@tariq1890 tariq1890 changed the title Skopeo multi-arch issue Fix[precompiled-arm]: Use oci: directory transport with --all flag instead of oci-archive Aug 11, 2026
@abrarshivani
abrarshivani force-pushed the skopeo-multiarch-issue branch from c857c08 to e89d63f Compare September 2, 2026 17:43
@abrarshivani abrarshivani changed the title Fix[precompiled-arm]: Use oci: directory transport with --all flag instead of oci-archive Fix precompiled image publish silently dropping non-native-arch manifests Sep 2, 2026
@abrarshivani

abrarshivani commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Verification:

  1. skopeo copy oci-archive:file.tar docker://... without --all doesn't error on a multi-platform (amd64+arm64) archive — it silently collapses to a single manifest, matching whichever architecture the copy happened to run on. The registry only ever received one architecture, with nothing to flag it.

  2. Reproduced this locally with the exact skopeo version CI installs (1.13.3+ds1-2ubuntu0.24.04.3, from a real scheduled run's logs), in a matching Ubuntu 24.04 container, against a real multi-arch OCI archive built the same way this workflow builds one.

  3. Confirmed the fix: adding --all to the existing oci-archive: copy publishes the full manifest list for both architectures. Extracting to an oci: directory first, as the original approach did, turned out not to be necessary.

  4. Pushed for real to ghcr.io (the actual destination registry), to a scratch tag under a personal namespace, and ran the verification logic against it — local and remote matched exactly for both amd64 and arm64.

  5. Not yet verified: the real Publish image step is gated to github.ref == 'refs/heads/main', so nothing here has run inside an actual GitHub Actions job. First real execution is the next scheduled run on main after merge.

@sylvesterkaczmarek sylvesterkaczmarek left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The index comparison keeps only architecture and digest, so platform metadata such as os and variant is ignored. Those fields live on the index descriptor rather than the child manifest digest, so a published index can carry the right digest under the wrong platform descriptor and still pass this check. Compare the full relevant platform tuple plus digest and add an ARM variant regression.

skopeo copy oci-archive:file.tar docker://... without --all doesn't
error on a multi-platform (amd64+arm64) archive, it silently collapses
to a single current-arch manifest, so the published image only had
one architecture even though the build produced both. --all on the
existing oci-archive: source is sufficient; no need to extract the
archive to an oci: directory first.

skopeo copy exiting 0 isn't proof the full index was published, so
also compare the local artifact's manifest against what's actually
live in the registry after the push. verify-manifest-match.sh handles
both shapes this repo's build matrix produces: an index (multi-arch
legs) compares platform tuple plus digest per entry, filtering out
attestation manifests; a plain manifest (the amd64-only legs) compares
config and layer digests. Each side determines its own shape
independently and rejects empty or degenerate input, so a shape
mismatch between local and remote reports as a clean failure instead
of a jq crash, and garbage input can't pass by comparing two empty
results to each other.

Signed-off-by: Abrar Shivani <ashivani@nvidia.com>
A real skopeo copy only ever exercises the happy path (a healthy copy
always matches its source), so the failure-detection branch needs
deliberately mismatched fixtures to prove it actually fires. Covers
matching and mismatched indexes, a dropped architecture, a crossed
architecture/variant/digest pairing that an arch-only comparison key
couldn't have caught, matching and mismatched single-arch manifests, a
local/remote shape mismatch, an index that resolves to no real
platforms, and empty input.

precompiled.yaml only triggers on schedule/workflow_dispatch, so wire
this into image.yaml instead: the one workflow in this repo that
actually runs on pull requests, as its own lightweight job rather than
inside the existing build matrix.

Signed-off-by: Abrar Shivani <ashivani@nvidia.com>
@abrarshivani
abrarshivani force-pushed the skopeo-multiarch-issue branch from 9557886 to b266c3e Compare September 2, 2026 22:30
@abrarshivani

Copy link
Copy Markdown
Contributor

@sylvesterkaczmarek Thanks for catching this. Updated the comparison to include the full platform tuple (os, architecture, variant) along with the digest, and added a regression test that verifies crossed variant/digest mappings are rejected.

@rajathagasthya rajathagasthya left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for identifying the root cause @abrarshivani!

Comment thread .github/workflows/image.yaml

@sylvesterkaczmarek sylvesterkaczmarek left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Re-reviewed the platform-identity finding. Index summaries now compare OS, architecture, variant and digest, and the added ARM-variant regression covers the crossed-variant case I raised. My previous blocker is resolved.

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.

Skopeo multi-arch publish issue

4 participants