diff --git a/scripts/lib/actions-artifact-archive.mjs b/scripts/lib/actions-artifact-archive.mjs index 8561175ac122..0e6bc0e33124 100644 --- a/scripts/lib/actions-artifact-archive.mjs +++ b/scripts/lib/actions-artifact-archive.mjs @@ -13,6 +13,7 @@ const ARTIFACT_DIGEST_RE = /^sha256:[0-9a-f]{64}$/u; const ARTIFACT_NAME_RE = /^[A-Za-z0-9][A-Za-z0-9_.-]*$/u; const COMMIT_SHA_RE = /^[0-9a-f]{40}$/u; const REPOSITORY_RE = /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/u; +const ACTIVE_SAME_RUN_STATUSES = new Set(["in_progress", "waiting"]); const SUPPORTED_ZIP_FLAGS = 0x0808; const ZIP_DATA_DESCRIPTOR_FLAG = 0x0008; const ZIP_UTF8_FLAG = 0x0800; @@ -736,8 +737,10 @@ export function validateActionsArtifactBinding(params) { throw new Error("Actions workflow run does not match the immutable publication tuple."); } } else if (expected.runAttempt === expected.consumerRunAttempt) { - if (run.status !== "in_progress" || run.conclusion !== null) { - throw new Error("Current producer workflow attempt must still be in progress."); + // Environment protection reports the active workflow as waiting until the + // approval transition propagates, even while the approved consumer runs. + if (!ACTIVE_SAME_RUN_STATUSES.has(run.status) || run.conclusion !== null) { + throw new Error("Current producer workflow attempt must still be active."); } } else if ( run.status !== "completed" || diff --git a/test/scripts/plugin-publication-artifact.test.ts b/test/scripts/plugin-publication-artifact.test.ts index e2335be2a4a9..41574d466f75 100644 --- a/test/scripts/plugin-publication-artifact.test.ts +++ b/test/scripts/plugin-publication-artifact.test.ts @@ -749,6 +749,23 @@ describe("plugin publication artifact", () => { ).toThrow("producer job did not complete successfully"); }); + it("accepts an environment-waiting current producer attempt", () => { + const fixture = createFixture(); + const workflowRun = JSON.parse(readFileSync(fixture.workflowRunPath, "utf8")); + workflowRun.status = "waiting"; + workflowRun.conclusion = null; + writeFileSync(fixture.workflowRunPath, `${JSON.stringify(workflowRun)}\n`); + + expect( + verifyFixture(fixture, { + consumerRunAttempt: RUN_ATTEMPT, + producerJobName: PRODUCER_JOB_NAME, + runStatePolicy: "same-run-producer-success", + workflowJobsMetadataPath: fixture.workflowJobsPath, + }), + ).toMatchObject({ producerRunAttempt: RUN_ATTEMPT, producerRunId: RUN_ID }); + }); + it("retries bounded metadata, attempt, and archive failures against the exact run attempt", async () => { const zip = createZip([{ bytes: Buffer.from("proof"), name: "proof.txt" }]); const artifactMetadata = {