diff --git a/scripts/github/find-reusable-release-validation.sh b/scripts/github/find-reusable-release-validation.sh index 53cacf241d6a..f7ffeb747e82 100755 --- a/scripts/github/find-reusable-release-validation.sh +++ b/scripts/github/find-reusable-release-validation.sh @@ -218,22 +218,33 @@ for ((index = 0; index < run_count; index += 1)); do and (.root.targetSha | type == "string" and test("^[0-9a-f]{40}$")) and (.root.artifact.digest | type == "string" and test("^sha256:[0-9a-f]{64}$")) and all($record.current, $record.root; - .producerOnTrustedMainLineage == true - and .workflowFullRef == "refs/heads/main" + . as $parent + | .producerOnTrustedMainLineage == true and .workflowRefType == "branch" and .workflowPath == ".github/workflows/full-release-validation.yml" + and .workflowFullRef == ("refs/heads/" + .workflowRef) and .workflowQualifiedPath == - ".github/workflows/full-release-validation.yml@refs/heads/main" + (".github/workflows/full-release-validation.yml@" + .workflowFullRef) and ( .workflowRunPath == ".github/workflows/full-release-validation.yml" - or .workflowRunPath == - ".github/workflows/full-release-validation.yml@refs/heads/main" + or .workflowRunPath == .workflowQualifiedPath ) and ( - (.manifestVersion == 3 and .workflowRefProof == "manifest-v3-branch") + ( + .workflowRef == "main" + and ( + (.manifestVersion == 3 and .workflowRefProof == "manifest-v3-branch") + or ( + .manifestVersion == 2 + and .workflowRefProof == "legacy-v2-main-ancestry" + ) + ) + ) or ( - .manifestVersion == 2 - and .workflowRefProof == "legacy-v2-main-ancestry" + .manifestVersion == 3 + and .workflowRefProof == "manifest-v3-sha-pinned-main-ancestry" + and (.workflowRef | test("^release-ci/[0-9a-f]{12}-[1-9][0-9]*$")) + and (.workflowRef | startswith("release-ci/\($parent.workflowSha[0:12])-")) ) ) ) diff --git a/test/scripts/find-reusable-release-validation.test.ts b/test/scripts/find-reusable-release-validation.test.ts index 2ea11c1299fb..fc75854cf4df 100644 --- a/test/scripts/find-reusable-release-validation.test.ts +++ b/test/scripts/find-reusable-release-validation.test.ts @@ -178,17 +178,24 @@ function normalizedEvidence(options: { targetSha: string; validationInputs?: Record | null; verifierSha?: string | null; + workflowRef?: string; }): NormalizedEvidence { const runId = options.runId ?? "111"; const producerSha = options.producerSha ?? PRODUCER_SHA; const releaseProfile = options.releaseProfile ?? "full"; const soak = options.soak ?? true; + const workflowRef = options.workflowRef ?? "main"; + const workflowFullRef = `refs/heads/${workflowRef}`; + const shaPinned = workflowRef.startsWith("release-ci/"); const validationInputs = options.validationInputs === undefined ? DEFAULT_INPUTS : options.validationInputs; const manifest = { - version: 2, + version: shaPinned ? 3 : 2, workflowName: "Full Release Validation", - workflowRef: "main", + workflowRef, + workflowSha: producerSha, + workflowFullRef, + workflowRefType: "branch", runId, runAttempt: "2", targetRef: "release/2026.7.1", @@ -224,20 +231,24 @@ function normalizedEvidence(options: { }, conclusion: "success", manifest, - manifestVersion: 2, + manifestVersion: shaPinned ? 3 : 2, runAttempt: 2, runId, status: "completed", targetSha: options.targetSha, url: `https://example.test/runs/${runId}`, producerOnTrustedMainLineage: true, - workflowFullRef: "refs/heads/main", + workflowFullRef, workflowPath: ".github/workflows/full-release-validation.yml", - workflowQualifiedPath: ".github/workflows/full-release-validation.yml@refs/heads/main", - workflowRef: "main", - workflowRefProof: "legacy-v2-main-ancestry", + workflowQualifiedPath: `.github/workflows/full-release-validation.yml@${workflowFullRef}`, + workflowRef, + workflowRefProof: shaPinned + ? "manifest-v3-sha-pinned-main-ancestry" + : "legacy-v2-main-ancestry", workflowRefType: "branch", - workflowRunPath: ".github/workflows/full-release-validation.yml", + workflowRunPath: shaPinned + ? `.github/workflows/full-release-validation.yml@${workflowFullRef}` + : ".github/workflows/full-release-validation.yml", workflowSha: producerSha, }; const roles = [ @@ -268,7 +279,7 @@ function normalizedEvidence(options: { dispatchNonce: `full-release-validation-${runId}-${sourceParentAttempt}${suffix}`, displayTitle: `${name} full-release-validation-${runId}-${sourceParentAttempt}${suffix}`, event: "workflow_dispatch", - headBranch: "main", + headBranch: workflowRef, parentJobId: `job-${role}`, path: `.github/workflows/${workflow}`, role, @@ -471,6 +482,34 @@ function parseOutput(output: string): Record { } describe("scripts/github/find-reusable-release-validation.sh", () => { + it("reuses strict direct-root evidence produced by a canonical SHA-pinned run", () => { + const { origin, priorSha } = createRepo(); + const clone = cloneHead(origin); + const producerSha = "d".repeat(40); + const producerRef = `release-ci/${producerSha.slice(0, 12)}-122`; + const record = normalizedEvidence({ + producerSha, + targetSha: priorSha, + workflowRef: producerRef, + }); + const { binDir, fixtures, validatorPath } = setUpFixtures([{ record, runId: "111" }]); + + const result = runResolver({ + binDir, + fixtures, + repoDir: clone, + targetSha: priorSha, + validatorPath, + workflowRef: `release-ci/${VERIFIER_SHA.slice(0, 12)}-123`, + }); + + expect(result.status).toBe(0); + expect(parseOutput(result.stdout)).toMatchObject({ + evidence_run_id: "111", + reuse: "true", + }); + }); + it("rejects noncanonical release refs and workflow SHAs outside trusted main", () => { const { origin, priorSha } = createRepo(); const clone = cloneHead(origin);