From 29b2f5016edb99676f344a7498d1a81ea4d22090 Mon Sep 17 00:00:00 2001 From: Richard McDaniel <1130888+rmcdaniel@users.noreply.github.com> Date: Thu, 5 Nov 2026 11:39:15 +0000 Subject: [PATCH 1/3] Cache the feature PHP base directly from its canonical registry --- .github/workflows/phpunit-feature.yml | 8 ++--- tests/Unit/ServerParityImagePullTest.mjs | 46 +++++++++++++++++++++++- 2 files changed, 49 insertions(+), 5 deletions(-) diff --git a/.github/workflows/phpunit-feature.yml b/.github/workflows/phpunit-feature.yml index e92cfb3e6..040654561 100644 --- a/.github/workflows/phpunit-feature.yml +++ b/.github/workflows/phpunit-feature.yml @@ -175,10 +175,10 @@ jobs: fetch-depth: 0 persist-credentials: false - - name: Cache public mirrored build inputs under their canonical image names - run: | - docker pull mirror.gcr.io/library/php:8.3-apache - docker tag mirror.gcr.io/library/php:8.3-apache php:8.3-apache + # A mirror's cached index can reference an unavailable child manifest. + # Cache the same official input directly, using the existing pull bound. + - name: Cache the canonical public PHP build input + run: bash scripts/ci/pull-images.sh php:8.3-apache - name: Key the source-free PHP tool images id: php_tools_key diff --git a/tests/Unit/ServerParityImagePullTest.mjs b/tests/Unit/ServerParityImagePullTest.mjs index b5deb9391..af898e806 100644 --- a/tests/Unit/ServerParityImagePullTest.mjs +++ b/tests/Unit/ServerParityImagePullTest.mjs @@ -2,7 +2,7 @@ // their first container (run 40164159608 lost 24 jobs to one "unknown blob"). import test from 'node:test'; import assert from 'node:assert/strict'; -import {chmodSync, mkdtempSync, readFileSync, writeFileSync} from 'node:fs'; +import {chmodSync, mkdtempSync, readFileSync, rmSync, writeFileSync} from 'node:fs'; import {tmpdir} from 'node:os'; import {join, resolve} from 'node:path'; import {spawnSync} from 'node:child_process'; @@ -96,3 +96,47 @@ test('empty arguments are skipped and malformed bounds are refused', () => { assert.deepEqual(result.calls, []); } }); + +function featureBaseCache(t, body, canonicalAvailable = true) { + const dir = mkdtempSync(join(tmpdir(), 'feature-base-cache-')); + t.after(() => rmSync(dir, {recursive: true, force: true})); + writeFileSync(join(dir, 'calls'), ''); + writeFileSync(join(dir, 'docker'), `#!/usr/bin/env bash +printf '%s\n' "$*" >> "$CALL_LOG" +case "$*" in + *mirror.gcr.io/library/php*) echo 'manifest not found' >&2; exit 1 ;; + 'pull --quiet php:8.3-apache') [[ "$CANONICAL_AVAILABLE" = true ]] ;; + *) exit 3 ;; +esac +`); + chmodSync(join(dir, 'docker'), 0o755); + const result = spawnSync('bash', ['-e', '-o', 'pipefail', '-c', body], { + encoding: 'utf8', timeout: 5000, + env: {...process.env, PATH: `${dir}:${process.env.PATH}`, CALL_LOG: join(dir, 'calls'), + CANONICAL_AVAILABLE: String(canonicalAvailable), DW_PULL_ATTEMPTS: '1', DW_PULL_BACKOFF_SECONDS: '0'}, + }); + return {...result, calls: readFileSync(join(dir, 'calls'), 'utf8').trim().split('\n').filter(Boolean)}; +} + +function featureBaseStep() { + const source = readFileSync(resolve('.github/workflows/phpunit-feature.yml'), 'utf8'); + const command = source.match(/- name: Cache the canonical public PHP build input\n {8}run: (.+)\n/)?.[1]; + assert.ok(command, 'the feature job caches its official PHP input before building'); + return command; +} + +test('feature base caching survives a missing mirror child without changing its canonical input', t => { + const predecessor = featureBaseCache(t, 'docker pull mirror.gcr.io/library/php:8.3-apache\ndocker tag mirror.gcr.io/library/php:8.3-apache php:8.3-apache'); + assert.equal(predecessor.status, 1); + assert.deepEqual(predecessor.calls, ['pull mirror.gcr.io/library/php:8.3-apache']); + const result = featureBaseCache(t, featureBaseStep()); + assert.equal(result.status, 0, result.stderr); + assert.deepEqual(result.calls, ['pull --quiet php:8.3-apache']); +}); + +test('feature base caching preserves canonical registry failure instead of proceeding to build', t => { + const result = featureBaseCache(t, featureBaseStep(), false); + assert.equal(result.status, 1); + assert.deepEqual(result.calls, ['pull --quiet php:8.3-apache']); + assert.match(result.stderr, /could not pull php:8\.3-apache after 1 attempts/); +}); From 2e367748e0043bdcac4ffcd160095d5d04a3ebb0 Mon Sep 17 00:00:00 2001 From: Richard McDaniel <1130888+rmcdaniel@users.noreply.github.com> Date: Thu, 5 Nov 2026 11:42:24 +0000 Subject: [PATCH 2/3] Pin the PHP index containing the hosted failing attestation companion --- .github/workflows/phpunit-feature.yml | 9 ++++++--- tests/Unit/ServerParityImagePullTest.mjs | 14 ++++++++------ 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/.github/workflows/phpunit-feature.yml b/.github/workflows/phpunit-feature.yml index 040654561..5127a5fcf 100644 --- a/.github/workflows/phpunit-feature.yml +++ b/.github/workflows/phpunit-feature.yml @@ -175,10 +175,13 @@ jobs: fetch-depth: 0 persist-credentials: false - # A mirror's cached index can reference an unavailable child manifest. - # Cache the same official input directly, using the existing pull bound. + # The mirror misses this index's amd64 attestation companion (90503a91). + # Retain that exact official index and runtime; use the canonical registry. - name: Cache the canonical public PHP build input - run: bash scripts/ci/pull-images.sh php:8.3-apache + run: | + php_base=php@sha256:3614314e50a9b17a0c317dfd100c6346d8881d6565e713556b7a4e8eb3ec4505 + bash scripts/ci/pull-images.sh "$php_base" + docker tag "$php_base" php:8.3-apache - name: Key the source-free PHP tool images id: php_tools_key diff --git a/tests/Unit/ServerParityImagePullTest.mjs b/tests/Unit/ServerParityImagePullTest.mjs index af898e806..158f0f185 100644 --- a/tests/Unit/ServerParityImagePullTest.mjs +++ b/tests/Unit/ServerParityImagePullTest.mjs @@ -9,6 +9,7 @@ import {spawnSync} from 'node:child_process'; const workflow = readFileSync(resolve('.github/workflows/server-parity.yml'), 'utf8'); const script = resolve('scripts/ci/pull-images.sh'); +const featureBase = 'php@sha256:3614314e50a9b17a0c317dfd100c6346d8881d6565e713556b7a4e8eb3ec4505'; function job(name) { const start = workflow.indexOf(`\n ${name}:\n`); @@ -105,7 +106,8 @@ function featureBaseCache(t, body, canonicalAvailable = true) { printf '%s\n' "$*" >> "$CALL_LOG" case "$*" in *mirror.gcr.io/library/php*) echo 'manifest not found' >&2; exit 1 ;; - 'pull --quiet php:8.3-apache') [[ "$CANONICAL_AVAILABLE" = true ]] ;; + 'pull --quiet ${featureBase}') [[ "$CANONICAL_AVAILABLE" = true ]] ;; + 'tag ${featureBase} php:8.3-apache') exit 0 ;; *) exit 3 ;; esac `); @@ -120,9 +122,9 @@ esac function featureBaseStep() { const source = readFileSync(resolve('.github/workflows/phpunit-feature.yml'), 'utf8'); - const command = source.match(/- name: Cache the canonical public PHP build input\n {8}run: (.+)\n/)?.[1]; + const command = source.match(/- name: Cache the canonical public PHP build input\n {8}run: \|\n((?: {10}[^\n]+\n)+)/)?.[1]; assert.ok(command, 'the feature job caches its official PHP input before building'); - return command; + return command.replace(/^ {10}/gm, ''); } test('feature base caching survives a missing mirror child without changing its canonical input', t => { @@ -131,12 +133,12 @@ test('feature base caching survives a missing mirror child without changing its assert.deepEqual(predecessor.calls, ['pull mirror.gcr.io/library/php:8.3-apache']); const result = featureBaseCache(t, featureBaseStep()); assert.equal(result.status, 0, result.stderr); - assert.deepEqual(result.calls, ['pull --quiet php:8.3-apache']); + assert.deepEqual(result.calls, [`pull --quiet ${featureBase}`, `tag ${featureBase} php:8.3-apache`]); }); test('feature base caching preserves canonical registry failure instead of proceeding to build', t => { const result = featureBaseCache(t, featureBaseStep(), false); assert.equal(result.status, 1); - assert.deepEqual(result.calls, ['pull --quiet php:8.3-apache']); - assert.match(result.stderr, /could not pull php:8\.3-apache after 1 attempts/); + assert.deepEqual(result.calls, [`pull --quiet ${featureBase}`]); + assert.ok(result.stderr.includes(`could not pull ${featureBase} after 1 attempts`)); }); From 284702f5b967093030d47499a4e00c049dffacc5 Mon Sep 17 00:00:00 2001 From: Richard McDaniel <1130888+rmcdaniel@users.noreply.github.com> Date: Thu, 5 Nov 2026 11:45:49 +0000 Subject: [PATCH 3/3] Keep the pinned PHP cache commands within the literal isolation model --- .github/workflows/phpunit-feature.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/phpunit-feature.yml b/.github/workflows/phpunit-feature.yml index 5127a5fcf..1acf9216d 100644 --- a/.github/workflows/phpunit-feature.yml +++ b/.github/workflows/phpunit-feature.yml @@ -179,9 +179,8 @@ jobs: # Retain that exact official index and runtime; use the canonical registry. - name: Cache the canonical public PHP build input run: | - php_base=php@sha256:3614314e50a9b17a0c317dfd100c6346d8881d6565e713556b7a4e8eb3ec4505 - bash scripts/ci/pull-images.sh "$php_base" - docker tag "$php_base" php:8.3-apache + bash scripts/ci/pull-images.sh php@sha256:3614314e50a9b17a0c317dfd100c6346d8881d6565e713556b7a4e8eb3ec4505 + docker tag php@sha256:3614314e50a9b17a0c317dfd100c6346d8881d6565e713556b7a4e8eb3ec4505 php:8.3-apache - name: Key the source-free PHP tool images id: php_tools_key