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:
parent
a55ed44394
commit
c2043b206a
52
.github/workflows/publish-server-image.yml
vendored
52
.github/workflows/publish-server-image.yml
vendored
@ -27,7 +27,7 @@ permissions:
|
|||||||
env:
|
env:
|
||||||
# docker/metadata-action otherwise truncates commit tags to seven collision-prone characters.
|
# docker/metadata-action otherwise truncates commit tags to seven collision-prone characters.
|
||||||
DOCKER_METADATA_SHORT_SHA_LENGTH: 40
|
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
|
DOCKER_BUILD_RECORD_UPLOAD: false
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
@ -162,22 +162,15 @@ jobs:
|
|||||||
cache-to: type=gha,mode=max,scope=${{ matrix.name }}-${{ matrix.arch }}
|
cache-to: type=gha,mode=max,scope=${{ matrix.name }}-${{ matrix.arch }}
|
||||||
provenance: mode=max
|
provenance: mode=max
|
||||||
sbom: true
|
sbom: true
|
||||||
- name: Record the architecture digest
|
- name: Tag digest for publish job handoff
|
||||||
id: digest
|
|
||||||
env:
|
env:
|
||||||
DIGEST: ${{ steps.build.outputs.digest }}
|
DIGEST: ${{ steps.build.outputs.digest }}
|
||||||
|
IMAGE: ghcr.io/${{ steps.ns.outputs.repo }}/${{ matrix.name }}
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
digest_dir="${RUNNER_TEMP}/digest"
|
docker buildx imagetools create \
|
||||||
mkdir -p "$digest_dir"
|
-t "${IMAGE}:ci-${GITHUB_RUN_ID}-${{ matrix.name }}-${{ matrix.arch }}" \
|
||||||
touch "${digest_dir}/${DIGEST#sha256:}"
|
"${IMAGE}@${DIGEST}"
|
||||||
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
|
|
||||||
|
|
||||||
publish:
|
publish:
|
||||||
name: Publish ${{ matrix.name }} image
|
name: Publish ${{ matrix.name }} image
|
||||||
@ -198,14 +191,6 @@ jobs:
|
|||||||
- name: updater
|
- name: updater
|
||||||
- name: computer
|
- name: computer
|
||||||
steps:
|
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
|
- uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0
|
||||||
- name: Log in to GHCR
|
- name: Log in to GHCR
|
||||||
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0
|
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0
|
||||||
@ -217,6 +202,21 @@ jobs:
|
|||||||
id: ns
|
id: ns
|
||||||
# GHCR rejects repository names with capitals; the GitHub repository slug has them.
|
# GHCR rejects repository names with capitals; the GitHub repository slug has them.
|
||||||
run: echo "repo=${GITHUB_REPOSITORY,,}" >> "$GITHUB_OUTPUT"
|
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
|
- id: meta
|
||||||
uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0
|
uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0
|
||||||
with:
|
with:
|
||||||
@ -280,3 +280,13 @@ jobs:
|
|||||||
subject-name: ghcr.io/${{ steps.ns.outputs.repo }}/${{ matrix.name }}
|
subject-name: ghcr.io/${{ steps.ns.outputs.repo }}/${{ matrix.name }}
|
||||||
subject-digest: ${{ steps.manifest.outputs.digest }}
|
subject-digest: ${{ steps.manifest.outputs.digest }}
|
||||||
push-to-registry: true
|
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
|
||||||
|
|||||||
@ -55,6 +55,13 @@ describe("server image publish workflow", () => {
|
|||||||
expect(workflowText).toContain("RUNNER_TEMP");
|
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", () => {
|
it("names every image under the lowercase repository namespace", () => {
|
||||||
expect(workflowText).not.toContain(`ghcr.io/\${{ github.repository }}`);
|
expect(workflowText).not.toContain(`ghcr.io/\${{ github.repository }}`);
|
||||||
const references =
|
const references =
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user