From f4ae478d33a56edbcb4c2d4ca50bc16714814625 Mon Sep 17 00:00:00 2001 From: Prekshi Vyas Date: Thu, 30 Jul 2026 17:06:37 -0700 Subject: [PATCH] fix(ci): clear managed image platform pulls --- .github/workflows/managed-images.yaml | 3 ++ .../managed-image-publication-barrier.ts | 15 +++++++++ ...managed-image-publication-workflow.test.ts | 32 +++++++++++++++++++ 3 files changed, 50 insertions(+) diff --git a/.github/workflows/managed-images.yaml b/.github/workflows/managed-images.yaml index bb64cbb452..affda75f20 100644 --- a/.github/workflows/managed-images.yaml +++ b/.github/workflows/managed-images.yaml @@ -811,6 +811,9 @@ jobs: DOCKER_CONFIG="$anonymous_config" docker pull \ --platform "$platform" \ "$cohort_reference" + # The classic Docker image store cannot retain two platform + # variants under one multi-platform digest reference. + docker image rm "$cohort_reference" done done < <(jq -c '.[]' "$cohort_manifests") diff --git a/test/helpers/managed-image-publication-barrier.ts b/test/helpers/managed-image-publication-barrier.ts index 550c016c03..30fd1ef787 100644 --- a/test/helpers/managed-image-publication-barrier.ts +++ b/test/helpers/managed-image-publication-barrier.ts @@ -141,6 +141,7 @@ export function runManagedImagePromotion(script: string, failCohortAgent = ""): const root = fs.mkdtempSync(path.join(os.tmpdir(), "nemoclaw-managed-promotion-")); const bin = path.join(root, "bin"); const calls = path.join(root, "docker-calls"); + const pulledReferences = path.join(root, "docker-pulled-references"); const candidateSet = path.join(root, "candidate-set.json"); const contracts = path.join(root, "managed-image-contracts"); const digest = `sha256:${"f".repeat(64)}`; @@ -163,6 +164,19 @@ export function runManagedImagePromotion(script: string, failCohortAgent = ""): `#!/usr/bin/env bash set -euo pipefail printf '%s\\n' "$*" >> "$DOCKER_CALLS" +if [ "$1" = "pull" ]; then + reference="\${@: -1}" + if grep -Fxq "$reference" "$DOCKER_PULLED_REFERENCES" 2>/dev/null; then + printf 'cannot overwrite digest %s\\n' "\${reference##*@}" >&2 + exit 92 + fi + printf '%s\\n' "$reference" >> "$DOCKER_PULLED_REFERENCES" +elif [ "$1" = "image" ] && [ "$2" = "rm" ]; then + reference="$3" + remaining="\${DOCKER_PULLED_REFERENCES}.remaining" + grep -Fvx "$reference" "$DOCKER_PULLED_REFERENCES" > "$remaining" || true + mv "$remaining" "$DOCKER_PULLED_REFERENCES" +fi if [ -n "\${FAIL_COHORT_AGENT:-}" ] && [[ "$*" == *"imagetools create"* ]] && [[ "$*" == *"/\${FAIL_COHORT_AGENT}-sandbox:cohort-"* ]]; then @@ -189,6 +203,7 @@ fi ...process.env, CANDIDATE_SET: candidateSet, DOCKER_CALLS: calls, + DOCKER_PULLED_REFERENCES: pulledReferences, FAIL_COHORT_AGENT: failCohortAgent, GITHUB_REPOSITORY: repository, GITHUB_RUN_ATTEMPT: runAttempt, diff --git a/test/managed-image-publication-workflow.test.ts b/test/managed-image-publication-workflow.test.ts index 7a4d487e40..cbad5ceee7 100644 --- a/test/managed-image-publication-workflow.test.ts +++ b/test/managed-image-publication-workflow.test.ts @@ -572,6 +572,38 @@ describe("complete managed-image publication workflow", () => { expect(runPublicationBarrier(barrier.run ?? "").status).toBe(0); }); + it("removes each anonymously pulled digest reference before the next platform pull", () => { + const promotion = required( + step( + managedPromoter(readWorkflow("managed-images.yaml")), + "Promote validated multi-platform managed image cohort", + ).run, + "managed image promotion script is missing", + ); + const result = runManagedImagePromotion(promotion); + const collision = runManagedImagePromotion( + promotion.replace('docker image rm "$cohort_reference"', ":"), + ); + const digest = `sha256:${"f".repeat(64)}`; + const anonymousPullCalls = result.calls.filter( + (call) => call.startsWith("pull --platform") || call.startsWith("image rm"), + ); + const expectedCalls = [...publicationAgents].sort().flatMap((agent) => { + const reference = `ghcr.io/nvidia/nemoclaw/${agent}-sandbox@${digest}`; + return [ + `pull --platform linux/amd64 ${reference}`, + `image rm ${reference}`, + `pull --platform linux/arm64 ${reference}`, + `image rm ${reference}`, + ]; + }); + + expect(collision.status).toBe(92); + expect(collision.stderr).toContain(`cannot overwrite digest ${digest}`); + expect(result.status, result.stderr).toBe(0); + expect(anonymousPullCalls).toEqual(expectedCalls); + }); + it("stages all multi-platform cohort aliases before moving the sole root pointer (#7744)", () => { const promotion = required( step(