ci: pass publish-server-image digests via GHCR handoff tags

Build jobs already push each architecture by digest. Tag those manifests
with ephemeral ci-${run_id}-${image}-${arch} refs instead of uploading
digest marker files to Actions artifact storage, which is blocked by the
account-wide quota.

Publish resolves handoff tags back into RUNNER_TEMP digest markers, then
assembles semver/edge tags and attestation as before. Handoff tags are
removed after publish with continue-on-error.

Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
This commit is contained in:
Cursor Agent 2026-10-06 02:27:10 +00:00
parent a55ed44394
commit c2043b206a
No known key found for this signature in database
2 changed files with 38 additions and 21 deletions

View File

@ -27,7 +27,7 @@ permissions:
env:
# docker/metadata-action otherwise truncates commit tags to seven collision-prone characters.
DOCKER_METADATA_SHORT_SHA_LENGTH: 40
# build-push-action v6 uploads large *.dockerbuild records by default; publish only needs digest artifacts.
# build-push-action v6 uploads large *.dockerbuild records by default; publish reads digests from GHCR handoff tags.
DOCKER_BUILD_RECORD_UPLOAD: false
concurrency:
@ -162,22 +162,15 @@ jobs:
cache-to: type=gha,mode=max,scope=${{ matrix.name }}-${{ matrix.arch }}
provenance: mode=max
sbom: true
- name: Record the architecture digest
id: digest
- name: Tag digest for publish job handoff
env:
DIGEST: ${{ steps.build.outputs.digest }}
IMAGE: ghcr.io/${{ steps.ns.outputs.repo }}/${{ matrix.name }}
run: |
set -euo pipefail
digest_dir="${RUNNER_TEMP}/digest"
mkdir -p "$digest_dir"
touch "${digest_dir}/${DIGEST#sha256:}"
echo "dir=${digest_dir}" >> "$GITHUB_OUTPUT"
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: digest-${{ matrix.name }}-${{ matrix.arch }}
path: ${{ steps.digest.outputs.dir }}/*
if-no-files-found: error
retention-days: 1
docker buildx imagetools create \
-t "${IMAGE}:ci-${GITHUB_RUN_ID}-${{ matrix.name }}-${{ matrix.arch }}" \
"${IMAGE}@${DIGEST}"
publish:
name: Publish ${{ matrix.name }} image
@ -198,14 +191,6 @@ jobs:
- name: updater
- name: computer
steps:
- name: Prepare an isolated digest directory
id: digest_dir
run: echo "path=${RUNNER_TEMP}/digests" >> "$GITHUB_OUTPUT"
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
pattern: digest-${{ matrix.name }}-*
path: ${{ steps.digest_dir.outputs.path }}
merge-multiple: true
- uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0
- name: Log in to GHCR
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0
@ -217,6 +202,21 @@ jobs:
id: ns
# GHCR rejects repository names with capitals; the GitHub repository slug has them.
run: echo "repo=${GITHUB_REPOSITORY,,}" >> "$GITHUB_OUTPUT"
- name: Collect architecture digests from registry handoff tags
id: digest_dir
env:
IMAGE: ghcr.io/${{ steps.ns.outputs.repo }}/${{ matrix.name }}
IMAGE_NAME: ${{ matrix.name }}
run: |
set -euo pipefail
digest_dir="${RUNNER_TEMP}/digests"
mkdir -p "$digest_dir"
for arch in amd64; do
handoff="${IMAGE}:ci-${GITHUB_RUN_ID}-${IMAGE_NAME}-${arch}"
digest="$(docker buildx imagetools inspect "$handoff" --format '{{.Manifest.Digest}}')"
touch "${digest_dir}/${digest#sha256:}"
done
echo "path=${digest_dir}" >> "$GITHUB_OUTPUT"
- id: meta
uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0
with:
@ -280,3 +280,13 @@ jobs:
subject-name: ghcr.io/${{ steps.ns.outputs.repo }}/${{ matrix.name }}
subject-digest: ${{ steps.manifest.outputs.digest }}
push-to-registry: true
- name: Remove CI handoff tags
continue-on-error: true
env:
IMAGE: ghcr.io/${{ steps.ns.outputs.repo }}/${{ matrix.name }}
IMAGE_NAME: ${{ matrix.name }}
run: |
set -euo pipefail
for arch in amd64; do
docker buildx imagetools rm "${IMAGE}:ci-${GITHUB_RUN_ID}-${IMAGE_NAME}-${arch}"
done

View File

@ -55,6 +55,13 @@ describe("server image publish workflow", () => {
expect(workflowText).toContain("RUNNER_TEMP");
});
it("passes build digests to publish through GHCR handoff tags, not Actions artifact storage", () => {
expect(workflowText).not.toContain("actions/upload-artifact@");
expect(workflowText).not.toContain("actions/download-artifact@");
expect(workflowText).toContain("Tag digest for publish job handoff");
expect(workflowText).toContain("ci-${GITHUB_RUN_ID}-");
});
it("names every image under the lowercase repository namespace", () => {
expect(workflowText).not.toContain(`ghcr.io/\${{ github.repository }}`);
const references =