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
24 changes: 23 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -112,10 +112,32 @@ jobs:
run: pnpm verify:package

- name: Publish to npm (with provenance)
run: pnpm publish --access public --no-git-checks
# `npm publish`, not `pnpm publish`: pnpm attempts its own OIDC token exchange first, and
# when that 404s (no trusted publisher configured) it falls back to the token and silently
# SKIPS provenance — which is exactly how 0.1.0 shipped unattested while this step printed
# success. The npm CLI generates the Sigstore attestation directly from the Actions OIDC
# identity granted by `id-token: write`.
run: npm publish --access public --provenance
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

- name: Verify the published version actually carries provenance
# A step named «with provenance» that nobody checks is a claim, not a fact — the previous
# release proved it. The registry is the only authority on what was attached, so ask it.
# Registry reads for a fresh publish can lag, hence the bounded retry rather than one shot.
run: |
for attempt in $(seq 1 20); do
body=$(curl -sf "https://registry.npmjs.org/-/npm/v1/attestations/@trdlabs/engine@${VERSION}" || true)
if echo "$body" | grep -q 'slsa.dev/provenance'; then
echo "provenance attestation present for ${VERSION}"
exit 0
fi
sleep 15
done
echo "PUBLISHED WITHOUT PROVENANCE: no attestation for @trdlabs/engine@${VERSION}"
echo "npm is immutable, so this version cannot be fixed in place — cut the next patch."
exit 1

- name: Tag the release (secondary release note)
run: |
git tag "v${VERSION}"
Expand Down
6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
{
"name": "@trdlabs/engine",
"version": "0.1.0",
"version": "0.1.1",
"description": "One deterministic execution core (decision → risk → pending order → fill → portfolio → canonical trace) shared by backtester and platform.",
"license": "Apache-2.0",
"repository": {
"type": "git",
"url": "git+https://github.com/trdlabs/engine.git"
},
"type": "module",
"engines": {
"node": ">=22"
Expand Down
6 changes: 6 additions & 0 deletions scripts/assert-version-publishable.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ if (pkg.version === '0.0.0') {
if (pkg.publishConfig?.provenance !== true) {
problems.push('publishConfig.provenance must be true (OIDC provenance, sdk parity)');
}
// npm documents a matching public `repository` as a prerequisite for provenance. Without it the
// publish still succeeds — it just silently ships no attestation, which is how `0.1.0` went out
// claiming provenance it did not have.
if (typeof pkg.repository?.url !== 'string' || !pkg.repository.url.includes('github.com/trdlabs/engine')) {
problems.push('repository.url must point at github.com/trdlabs/engine (npm provenance prerequisite)');
}
if (pkg.license !== 'Apache-2.0') {
problems.push(`license must be Apache-2.0 (got "${pkg.license}")`);
}
Expand Down
Loading