Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/token-free-npm-mirror.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@operatorstack/yield": patch
---

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.
9 changes: 2 additions & 7 deletions .github/workflows/npm-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -209,19 +209,14 @@ 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
if npm view "@operatorstack/create-yield@${VERSION}" version >/dev/null 2>&1; then
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:
Expand Down Expand Up @@ -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'
Expand Down
13 changes: 10 additions & 3 deletions .github/workflows/verify.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
37 changes: 11 additions & 26 deletions scripts/check-release-control.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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") &&
Expand Down Expand Up @@ -221,6 +223,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"),
Expand Down Expand Up @@ -355,31 +363,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`,
)
}
Expand Down