From e3b45825bf79236bda6c2e9a875de5c79e819ac3 Mon Sep 17 00:00:00 2001 From: Slate Rehm Date: Sun, 30 Aug 2026 14:33:32 -0500 Subject: [PATCH 1/2] fix(ci): retry npm provenance lookup --- .github/workflows/release.yml | 27 +++++++++++++++++++++------ apps/api/package.json | 2 +- apps/cli/package.json | 2 +- package.json | 2 +- packages/client/package.json | 2 +- packages/crypto/package.json | 2 +- packages/protocol/package.json | 2 +- 7 files changed, 27 insertions(+), 12 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 53a7e19..569c378 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -337,13 +337,28 @@ jobs: if (metadata.dist?.integrity !== process.env.NPM_INTEGRITY) { throw new Error("The published npm package has different bytes."); } - const attestationsResponse = await fetch(metadata.dist.attestations.url, { - signal: AbortSignal.timeout(15_000), - }); - if (!attestationsResponse.ok) { - throw new Error(`The npm attestation registry returned HTTP ${attestationsResponse.status}.`); + let attestations; + for (let attempt = 1; attempt <= 12; attempt++) { + const response = await fetch(metadata.dist.attestations.url, { + signal: AbortSignal.timeout(15_000), + }); + if (response.ok) { + attestations = await response.json(); + if ( + attestations.attestations?.some( + (item) => item.predicateType === "https://slsa.dev/provenance/v1", + ) + ) { + break; + } + } else if (response.status !== 404) { + throw new Error(`The npm attestation registry returned HTTP ${response.status}.`); + } + if (attempt === 12) { + throw new Error("The npm provenance did not become available."); + } + await new Promise((resolve) => setTimeout(resolve, 5_000)); } - const attestations = await attestationsResponse.json(); const provenance = attestations.attestations?.find( (item) => item.predicateType === "https://slsa.dev/provenance/v1", ); diff --git a/apps/api/package.json b/apps/api/package.json index 431db8b..089dc15 100644 --- a/apps/api/package.json +++ b/apps/api/package.json @@ -1,6 +1,6 @@ { "name": "@secret-effects/api", - "version": "0.2.2", + "version": "0.2.3", "private": true, "type": "module", "dependencies": { diff --git a/apps/cli/package.json b/apps/cli/package.json index c8858a2..203c8d8 100644 --- a/apps/cli/package.json +++ b/apps/cli/package.json @@ -1,6 +1,6 @@ { "name": "@secret-effects/cli", - "version": "0.2.2", + "version": "0.2.3", "private": true, "type": "module", "bin": { diff --git a/package.json b/package.json index c42ba89..5ac37dd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "secret-effects", - "version": "0.2.2", + "version": "0.2.3", "private": true, "type": "module", "license": "MIT", diff --git a/packages/client/package.json b/packages/client/package.json index 6a261d9..d086004 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@paperkeel/secret-effects-client", - "version": "0.2.2", + "version": "0.2.3", "description": "Type-safe Secret Effects environment loading for Node.js and Cloudflare Workers", "type": "module", "license": "MIT", diff --git a/packages/crypto/package.json b/packages/crypto/package.json index 5ab9741..cdfac65 100644 --- a/packages/crypto/package.json +++ b/packages/crypto/package.json @@ -1,6 +1,6 @@ { "name": "@secret-effects/crypto", - "version": "0.2.2", + "version": "0.2.3", "type": "module", "license": "MIT", "exports": { diff --git a/packages/protocol/package.json b/packages/protocol/package.json index e511d20..38dd1f7 100644 --- a/packages/protocol/package.json +++ b/packages/protocol/package.json @@ -1,6 +1,6 @@ { "name": "@secret-effects/protocol", - "version": "0.2.2", + "version": "0.2.3", "type": "module", "license": "MIT", "exports": { From 34d8336ef2bb5a13675731ac9bc0c257c5694f3e Mon Sep 17 00:00:00 2001 From: Slate Rehm Date: Tue, 1 Sep 2026 17:11:17 -0500 Subject: [PATCH 2/2] fix(ci): retry prepare provenance and require payload Poll attestation reads in prepare and publish-npm until SLSA provenance includes a dsseEnvelope payload. Co-authored-by: Cursor --- .github/workflows/release.yml | 41 +++++++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 569c378..8ea0609 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -174,15 +174,34 @@ jobs: if (typeof attestationsUrl !== "string") { throw new Error(`${name}@${process.env.RELEASE_VERSION} has no npm provenance.`); } - const attestationsResponse = await fetch(attestationsUrl, { - signal: AbortSignal.timeout(15_000), - }); - if (!attestationsResponse.ok) { - throw new Error(`The npm attestation registry returned HTTP ${attestationsResponse.status}.`); + let attestations; + for (let attempt = 1; attempt <= 12; attempt++) { + const response = await fetch(attestationsUrl, { + signal: AbortSignal.timeout(15_000), + }); + if (response.ok) { + attestations = await response.json(); + if ( + attestations.attestations?.some( + (item) => + item.predicateType === "https://slsa.dev/provenance/v1" && + item.bundle?.dsseEnvelope?.payload, + ) + ) { + break; + } + } else if (response.status !== 404) { + throw new Error(`The npm attestation registry returned HTTP ${response.status}.`); + } + if (attempt === 12) { + throw new Error(`${name}@${process.env.RELEASE_VERSION} has no SLSA provenance.`); + } + await new Promise((resolve) => setTimeout(resolve, 5_000)); } - const attestations = await attestationsResponse.json(); const provenance = attestations.attestations?.find( - (item) => item.predicateType === "https://slsa.dev/provenance/v1", + (item) => + item.predicateType === "https://slsa.dev/provenance/v1" && + item.bundle?.dsseEnvelope?.payload, ); if (!provenance?.bundle?.dsseEnvelope?.payload) { throw new Error(`${name}@${process.env.RELEASE_VERSION} has no SLSA provenance.`); @@ -346,7 +365,9 @@ jobs: attestations = await response.json(); if ( attestations.attestations?.some( - (item) => item.predicateType === "https://slsa.dev/provenance/v1", + (item) => + item.predicateType === "https://slsa.dev/provenance/v1" && + item.bundle?.dsseEnvelope?.payload, ) ) { break; @@ -360,7 +381,9 @@ jobs: await new Promise((resolve) => setTimeout(resolve, 5_000)); } const provenance = attestations.attestations?.find( - (item) => item.predicateType === "https://slsa.dev/provenance/v1", + (item) => + item.predicateType === "https://slsa.dev/provenance/v1" && + item.bundle?.dsseEnvelope?.payload, ); if (!provenance?.bundle?.dsseEnvelope?.payload) { throw new Error("The npm package has no SLSA provenance.");