diff --git a/.github/scripts/tests/post-merge-runner-routing.test.mjs b/.github/scripts/tests/post-merge-runner-routing.test.mjs index 95c2a1c5ef..68a2cada62 100644 --- a/.github/scripts/tests/post-merge-runner-routing.test.mjs +++ b/.github/scripts/tests/post-merge-runner-routing.test.mjs @@ -10,7 +10,7 @@ const base = { ref: "refs/heads/master", event_name: "push", sha, }; const expectedJobs = { - "cloud-readiness.yml": ["artifacts", "source_verified", "ready"], + "cloud-readiness.yml": [], "cloud-artifacts.yml": ["dispatch_migrator"], "release-verify.yml": ["typecheck", "general_tests", "serialized_tests", "runner_workflow_evals", "verify_paperclip_runner", "build"], "runner-chaos-evals.yml": ["chaos_and_recovery"], @@ -81,3 +81,27 @@ for (const [file, expectedNames] of Object.entries(expectedJobs)) { }); } } + + +test("Cloud readiness bookkeeping never waits for the AWS verification fleet", () => { + const workflow = readFileSync(new URL("../../workflows/cloud-readiness.yml", import.meta.url), "utf8"); + const bodies = new Map(); + for (const [name, needs] of [ + ["artifacts", null], + ["source_verified", "[verify]"], + ["ready", "[verify, image, artifacts]"], + ]) { + const body = workflow.match(new RegExp(`^ ${name}:\\n([\\s\\S]*?)(?=^ [a-z_]+:|(?![\\s\\S]))`, "m"))?.[1]; + assert.ok(body, `missing ${name} job`); + bodies.set(name, body); + assert.match(body, /^ runs-on: ubuntu-latest$/m); + assert.doesNotMatch(body, /^ +continue-on-error:|^ +if:.*always\(\)/m); + assert.match(body, /^ if: github.repository == 'paperclipai\/paperclip' && github.ref == 'refs\/heads\/master'$/m); + assert.match(body, /^ +SOURCE_SHA: \$\{\{ github.sha \}\}$/m); + assert.equal(body.match(/^ needs: (.+)$/m)?.[1] ?? null, needs, `${name} prerequisites`); + } + assert.match(bodies.get("artifacts"), /^ run: node scripts\/cloud-readiness.mjs "\$SOURCE_SHA"$/m); + assert.match(bodies.get("source_verified"), /^ run: node --test scripts\/cloud-source-verification.test.mjs$/m); + assert.match(bodies.get("source_verified"), /echo "Cloud source verified v1: \$SOURCE_SHA"/); + assert.match(bodies.get("ready"), /echo "Cloud deployable v1: \$SOURCE_SHA"/); +}); diff --git a/.github/workflows/cloud-readiness.yml b/.github/workflows/cloud-readiness.yml index 002da29614..453cad2172 100644 --- a/.github/workflows/cloud-readiness.yml +++ b/.github/workflows/cloud-readiness.yml @@ -32,7 +32,8 @@ jobs: artifacts: if: github.repository == 'paperclipai/paperclip' && github.ref == 'refs/heads/master' name: Wait for exact-source cloud artifacts - runs-on: ${{ vars.AWS_POST_MERGE_CI_ENABLED == 'true' && github.repository == 'paperclipai/paperclip' && github.repository_id == '1170821064' && github.ref == 'refs/heads/master' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && 'runs-on/fleet=paperclip-post-merge-x64/env=public-ci' || 'ubuntu-latest' }} + # Bookkeeping must not wait for the AWS builders it observes. + runs-on: ubuntu-latest timeout-minutes: 35 permissions: contents: read @@ -55,7 +56,8 @@ jobs: name: Cloud source verified v1 needs: [verify] if: github.repository == 'paperclipai/paperclip' && github.ref == 'refs/heads/master' - runs-on: ${{ vars.AWS_POST_MERGE_CI_ENABLED == 'true' && github.repository == 'paperclipai/paperclip' && github.repository_id == '1170821064' && github.ref == 'refs/heads/master' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && 'runs-on/fleet=paperclip-post-merge-x64/env=public-ci' || 'ubuntu-latest' }} + # Bookkeeping must not wait for the AWS builders it observes. + runs-on: ubuntu-latest timeout-minutes: 5 permissions: contents: read @@ -81,7 +83,8 @@ jobs: name: Cloud deployable v1 needs: [verify, image, artifacts] if: github.repository == 'paperclipai/paperclip' && github.ref == 'refs/heads/master' - runs-on: ${{ vars.AWS_POST_MERGE_CI_ENABLED == 'true' && github.repository == 'paperclipai/paperclip' && github.repository_id == '1170821064' && github.ref == 'refs/heads/master' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && 'runs-on/fleet=paperclip-post-merge-x64/env=public-ci' || 'ubuntu-latest' }} + # Bookkeeping must not wait for the AWS builders it observes. + runs-on: ubuntu-latest timeout-minutes: 5 steps: - name: Record cloud readiness diff --git a/doc/RELEASE-AUTOMATION-SETUP.md b/doc/RELEASE-AUTOMATION-SETUP.md index 4817e5d7fc..5e5afc481d 100644 --- a/doc/RELEASE-AUTOMATION-SETUP.md +++ b/doc/RELEASE-AUTOMATION-SETUP.md @@ -399,3 +399,18 @@ fixture setup; it does not make a single test faster. The file-duration manifest also records the native Codex Runner integration suite's measured import and execution cost, so the existing file balancer accounts for it in both ordinary PR and release verification. + + +## Cloud readiness runner placement + +When AWS routing is enabled, Cloud image builds use `paperclip-cloud-build-x64` +and source verification uses `paperclip-post-merge-x64`. The artifact wait and +the `Cloud source verified v1` and `Cloud deployable v1` marker jobs run on +GitHub-hosted runners. These small jobs must not hold or wait for capacity in +the source-verification fleet. During a merge +burst, even a completed build must wait for its marker before consumers can +recognize readiness. + +Runner placement does not change readiness requirements: exact-source artifacts, +all source checks, and the image verification must still pass. The versioned +markers and their dependency gates are unchanged.