diff --git a/.github/workflows/release-image.yml b/.github/workflows/release-image.yml index 131c48d46..40e18dee9 100644 --- a/.github/workflows/release-image.yml +++ b/.github/workflows/release-image.yml @@ -175,8 +175,8 @@ jobs: fi # 5. Deterministic metadata: the build date is the tagged commit's - # own timestamp, so re-running the same release reproduces the - # same image config instead of minting a new digest per run. + # own timestamp, so the image label and the binary's build time + # are the same in every run of the release. BUILD_DATE=$(TZ=UTC git show -s --date=format-local:'%Y-%m-%dT%H:%M:%SZ' --format=%cd "$SHA") { @@ -287,8 +287,14 @@ jobs: VERSION=${{ needs.gate.outputs.version }} VCS_REF=${{ needs.gate.outputs.sha }} BUILD_DATE=${{ needs.gate.outputs.build_date }} - cache-from: type=gha,scope=${{ matrix.arch }} - cache-to: type=gha,mode=max,scope=${{ matrix.arch }} + # No gha build cache, as in release-dev-image.yml. The image built + # here is the one that is scanned, pushed and signed, so a layer + # restored from the Actions cache would ship in a signed release + # (actions/cache-poisoning/poisonable-step). The cache did little + # for release builds: the cargo build layer's inputs change with + # every release, so it was restored only when the same release was + # built a second time, and exporting the cache took up to two and + # a half minutes per build. - name: Confirm the image really is ${{ matrix.arch }} # Cheap guard against a silent platform fallback: a mislabelled image @@ -415,13 +421,19 @@ jobs: CANDIDATE="sha-${SHA}" # Idempotent no-overwrite: if the candidate tag exists and holds - # EXACTLY what this run built, fall through to sign and propagate - # (a failed earlier run must be finishable by re-dispatch, or the - # failure-prone tail strands a published-but-unsigned candidate no - # re-dispatch can fix). Identity is compared by per-arch image - # config digest — the image ID, which docker save/load preserves - # and the smoke test certified — with NO registry mutation: a - # different artifact errors before anything is pushed. + # EXACTLY what this run built, fall through to sign and propagate, + # so a run that failed after pushing can still be finished. + # Identity is compared by per-arch image config digest (the image + # ID, which docker save/load preserves and the smoke test + # certified) with NO registry mutation: a different artifact + # errors before anything is pushed. The build has no layer cache, + # so a new dispatch builds images with different IDs and, if the + # earlier run pushed the candidate, stops here. To finish a failed + # run, use "Re-run failed jobs" on that run while its image + # artifacts are kept (retention-days: 1 above); this job then + # reloads the images it pushed and they match. Once the artifacts + # have expired the run cannot be finished this way; sign-image.yml + # covers signing a candidate that was pushed but not signed. if EXISTING=$(docker buildx imagetools inspect "${REPO}:${CANDIDATE}" --format '{{.Manifest.Digest}}' 2>/dev/null); then RAW=$(skopeo inspect --no-creds --raw "docker://${REPO}@${EXISTING}") MATCH=true