From aed5ab33c875468306f66090d8c83439a749ba68 Mon Sep 17 00:00:00 2001 From: bigboateng Date: Mon, 10 Aug 2026 14:26:28 +0100 Subject: [PATCH 1/2] Fix npm OIDC mirror publishing --- .changeset/token-free-npm-mirror.md | 5 +++++ .github/workflows/npm-publish.yml | 9 ++------- scripts/check-release-control.mjs | 31 +++++++---------------------- 3 files changed, 14 insertions(+), 31 deletions(-) create mode 100644 .changeset/token-free-npm-mirror.md diff --git a/.changeset/token-free-npm-mirror.md b/.changeset/token-free-npm-mirror.md new file mode 100644 index 0000000..87a0301 --- /dev/null +++ b/.changeset/token-free-npm-mirror.md @@ -0,0 +1,5 @@ +--- +"@operatorstack/yield": patch +--- + +Use npm trusted publishing without a token fallback and disable public provenance when mirroring exact npm archives to Artifact Registry. diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml index 1eb7a4c..95deb72 100644 --- a/.github/workflows/npm-publish.yml +++ b/.github/workflows/npm-publish.yml @@ -209,7 +209,6 @@ jobs: env: DIST_TAG: ${{ needs.resolve.outputs.dist_tag }} VERSION: ${{ needs.resolve.outputs.version }} - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} shell: bash run: | set -euo pipefail @@ -217,11 +216,7 @@ jobs: echo "@operatorstack/create-yield@${VERSION} already exists" else file="$(jq -r '.archives[] | select(.name == "@operatorstack/create-yield") | .file' dist/release-unit/npm/npm-release.json)" - if [[ -n "${NPM_TOKEN:-}" ]]; then - NODE_AUTH_TOKEN="$NPM_TOKEN" npm publish "dist/release-unit/npm/${file}" --tag "$DIST_TAG" - else - npm publish "dist/release-unit/npm/${file}" --tag "$DIST_TAG" - fi + npm publish "dist/release-unit/npm/${file}" --tag "$DIST_TAG" fi - name: Verify complete npm release unit env: @@ -462,7 +457,7 @@ jobs: run: | set -euo pipefail jq -r '.missing.npm[].file' "$RUNNER_TEMP/private-mirror-status.json" | while read -r file; do - npm publish "dist/release-unit/npm/${file}" --registry="$AR_NPM_URL" + npm publish "dist/release-unit/npm/${file}" --registry="$AR_NPM_URL" --provenance=false done - name: Mirror exact Python wheels if: steps.remote.outputs.python_state != 'matched' diff --git a/scripts/check-release-control.mjs b/scripts/check-release-control.mjs index 503bb05..aed160a 100644 --- a/scripts/check-release-control.mjs +++ b/scripts/check-release-control.mjs @@ -221,6 +221,12 @@ export async function checkReleaseControl(root = resolve(import.meta.dirname, ". raw["npm-publish.yml"].includes('npm publish "dist/release-unit/npm/${file}"'), "npm publisher must publish the verified archive, not a directory", ) + expect( + raw["npm-publish.yml"].includes( + 'npm publish "dist/release-unit/npm/${file}" --registry="$AR_NPM_URL" --provenance=false', + ), + "private npm mirroring must disable public-registry provenance", + ) expect( raw["npm-publish.yml"].indexOf("Publish SDK and CLI") < raw["npm-publish.yml"].indexOf("Publish npm initializer"), @@ -355,31 +361,8 @@ export async function checkReleaseControl(root = resolve(import.meta.dirname, ". "crates.io publishing must not use a bootstrap token", ) for (const [name, text] of Object.entries(raw)) { - let credentialSurface = text - if (name === "npm-publish.yml") { - const initializerStart = text.indexOf(" - name: Publish npm initializer") - const initializerEnd = text.indexOf(" - name: Verify complete npm release unit") - expect( - initializerStart >= 0 && initializerEnd > initializerStart, - "npm bootstrap credential must be confined to the initializer step", - ) - const initializer = text.slice(initializerStart, initializerEnd) - expect( - initializer.includes("NPM_TOKEN: ${{ secrets.NPM_TOKEN }}"), - "the initializer bootstrap credential must be explicitly scoped", - ) - expect( - initializer.includes( - 'NODE_AUTH_TOKEN="$NPM_TOKEN" npm publish "dist/release-unit/npm/${file}"', - ), - "the bootstrap credential must authenticate only the verified initializer archive", - ) - credentialSurface = text.slice(0, initializerStart) + text.slice(initializerEnd) - } expect( - !/NPM_TOKEN|NODE_AUTH_TOKEN|PYPI_TOKEN|secrets\.(npm|pypi)|password:/i.test( - credentialSurface, - ), + !/NPM_TOKEN|NODE_AUTH_TOKEN|PYPI_TOKEN|secrets\.(npm|pypi)|password:/i.test(text), `${name}: long-lived registry credentials are forbidden`, ) } From f47a04f8ac639ef0a62a95fbbbdfef11cf873c0d Mon Sep 17 00:00:00 2001 From: bigboateng Date: Mon, 10 Aug 2026 14:33:58 +0100 Subject: [PATCH 2/2] Freeze example CI runtime version --- .changeset/token-free-npm-mirror.md | 2 +- .github/workflows/verify.yml | 13 ++++++++++--- scripts/check-release-control.mjs | 6 ++++-- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/.changeset/token-free-npm-mirror.md b/.changeset/token-free-npm-mirror.md index 87a0301..1c69984 100644 --- a/.changeset/token-free-npm-mirror.md +++ b/.changeset/token-free-npm-mirror.md @@ -2,4 +2,4 @@ "@operatorstack/yield": patch --- -Use npm trusted publishing without a token fallback and disable public provenance when mirroring exact npm archives to Artifact Registry. +Use npm trusted publishing without a token fallback, disable public provenance when mirroring exact npm archives to Artifact Registry, and keep example CI on its fixture-declared Yield version. diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index 75644f7..7fcfae6 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -168,9 +168,16 @@ jobs: - run: npm ci --ignore-scripts - name: Run every example fixture run: | - base_tag="$(git tag --merged HEAD --list 'v[0-9]*' --sort=-v:refname | head -n1)" - test -n "$base_tag" - version="${base_tag#v}" + version="" + while IFS= read -r manifest; do + required="$(jq -er '.yield_version | select(type == "string" and length > 0)' "$manifest")" + if [[ -z "$version" ]]; then + version="$required" + else + test "$required" = "$version" + fi + done < <(find examples -name skill.json -type f -print | sort) + test -n "$version" go build -ldflags "-X main.version=$version" -o "$RUNNER_TEMP/yskill" ./cmd/yskill "$RUNNER_TEMP/yskill" test examples/investigate "$RUNNER_TEMP/yskill" test examples/release-checklist diff --git a/scripts/check-release-control.mjs b/scripts/check-release-control.mjs index aed160a..9ea127e 100644 --- a/scripts/check-release-control.mjs +++ b/scripts/check-release-control.mjs @@ -86,8 +86,10 @@ export async function checkReleaseControl(root = resolve(import.meta.dirname, ". ) expect( verify.jobs?.examples?.name === "Example workflows" && - raw["verify.yml"].includes('version="${base_tag#v}"'), - "example verification must stay pinned to the latest released Yield version", + raw["verify.yml"].includes("find examples -name skill.json") && + raw["verify.yml"].includes('version="$required"') && + !raw["verify.yml"].includes("base_tag="), + "example verification must use the version declared by the frozen fixtures", ) expect( !raw["verify.yml"].includes("evals/scripts/run.mjs") &&