Skip to content

fix(ci): publish npm releases through trusted OIDC#62

Merged
TimBeyer merged 2 commits into
masterfrom
agent/npm-trusted-publishing
Jul 13, 2026
Merged

fix(ci): publish npm releases through trusted OIDC#62
TimBeyer merged 2 commits into
masterfrom
agent/npm-trusted-publishing

Conversation

@TimBeyer

@TimBeyer TimBeyer commented Jul 13, 2026

Copy link
Copy Markdown
Owner

What changed

  • Generate release benchmark documentation on namespace-profile-default with Node 25 after lint, test, and build succeed.
  • Upload only README.md as a SHA-scoped, one-day artifact, download it outside the checkout, and copy only that file into the release worktree.
  • Run npm publishing on a supported GitHub-hosted runner with job-scoped contents: write and id-token: write.
  • Use checkout/setup-node v6, Node 24, npm 11, and disable package-manager caching for the privileged publish job.
  • Remove NPM_TOKEN and NODE_AUTH_TOKEN from the release environment.
  • Skip release-it 19's token-based npm preflight and override its benchmark hook only in CI, because the Namespace job already generated the README. Manual releases retain the existing benchmark hook.

Why

The failed Release run reached npm with the old automation token and was rejected with EOTP. npm trusted publishing authenticates npm publish through a short-lived OIDC identity, requires a GitHub-hosted runner and npm 11.5.1 or newer, and does not need a long-lived npm token.

The release hook also publishes fresh performance comparisons in the README. GitHub-hosted CPU performance is too variable for those measurements, so the workflow now keeps benchmark generation on the existing Namespace runner while isolating the actual OIDC publish on GitHub-hosted infrastructure.

release-it's default npm whoami preflight cannot observe the publish-time OIDC identity, so the workflow bypasses only that preflight. Its real versioning, changelog, commit, tag, push, GitHub release, and npm publish behavior remains active.

Security boundary

  • The Namespace benchmark job has only contents: read, with no secrets or OIDC permission.
  • Only non-executable README.md output crosses the job boundary.
  • The artifact is extracted under RUNNER_TEMP; the privileged job explicitly copies one expected file and verifies that no other tracked file changed.
  • Package build and publishing inputs still come from the checked-out event SHA on the GitHub-hosted job.

npm configuration required before merge

The dancing-links package's trusted publisher must match:

  • Owner: TimBeyer
  • Repository: dancing-links
  • Workflow filename: release.yml
  • Allowed action: npm publish
  • Environment: blank

Validation

  • Parsed .github/workflows/release.yml as YAML and asserted the runner, permission, dependency, artifact, and token-removal contract.
  • Verified Prettier formatting and git diff --check.
  • Verified release-it 19 parses --npm.skipChecks and the CI-only after:bump hook override correctly.
  • Release-it dry-runs prove CI runs formatting without rerunning benchmarks, while an ordinary/manual release retains update-benchmark-docs.
  • Verified release-it stages the tracked README via git add . --update.
  • Smoke-tested benchmark updater argument forwarding without generating local measurements.
  • npm test (48 passing).
  • npm run build.
  • npm run lint.
  • Independent workflow/release audit: no blockers.

The publish itself runs only after a push to master, so the final OIDC exchange is verified by the post-merge Release run.

References: npm trusted publishing, GitHub workflow artifacts, release-it OIDC setup.

Move the privileged release job to a supported GitHub-hosted runner and grant only that job contents and OIDC token permissions.

Use the current release actions, Node 24, uncached setup, and npm 11 so the job satisfies npm trusted-publishing requirements without silently adopting a future npm major.

Remove long-lived npm token variables and skip release-it 19's token-only npm whoami preflight. The actual npm publish command now exchanges the short-lived GitHub OIDC identity and generates provenance automatically.

Validated with YAML parsing, Prettier, release-it CLI argument parsing, 48 unit tests, build, and lint.
@github-actions

github-actions Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

🚀 Benchmark Results

Performance comparison against merge-base 513d1d5

bun-latest

dancing-links@4.3.7 compare-benchmarks
npm run build:dev && node built/scripts/compare-benchmarks.js baseline-results-bun-latest.json pr-results-bun-latest.json

dancing-links@4.3.7 build:dev
npm run clean && tsc -p tsconfig.dev.json

dancing-links@4.3.7 clean
rimraf built

🚀 Benchmark Results

All solutions to the sudoku

Library Baseline (ops/sec) PR (ops/sec) Change Performance
dancing-links (binary) 4,781.397 ±0.33% 4,539.664 ±0.43% -5.06% ⚠️ Minor regression
dancing-links (sparse) 15,701.403 ±0.23% 16,335.988 ±0.22% +4.04% ✅ Improvement
dancing-links generator 15,594.254 ±0.24% 15,954.251 ±0.26% +2.31% ✅ Improvement
dancing-links template 17,745.014 ±0.21% 17,936.986 ±0.21% +1.08% ➡️ No significant change

Finding one pentomino tiling on a 6x10 field

Library Baseline (ops/sec) PR (ops/sec) Change Performance
dancing-links (binary) 510.712 ±0.73% 536.394 ±0.51% +5.03% ✅ Improvement
dancing-links (sparse) 571.426 ±0.39% 566.498 ±0.47% -0.86% ➡️ No significant change
dancing-links generator 550.81 ±0.45% 573.364 ±0.51% +4.09% ✅ Improvement
dancing-links template 556.879 ±0.50% 587.532 ±0.40% +5.50% ✅ Improvement

Finding ten pentomino tilings on a 6x10 field

Library Baseline (ops/sec) PR (ops/sec) Change Performance
dancing-links (binary) 82.98 ±1.11% 80.868 ±1.17% -2.54% ⚠️ Minor regression
dancing-links (sparse) 84.614 ±0.80% 80.995 ±1.56% -4.28% ⚠️ Minor regression
dancing-links generator 83.213 ±0.93% 85.017 ±1.14% +2.17% ✅ Improvement
dancing-links template 85.414 ±0.66% 84.945 ±0.75% -0.55% ➡️ No significant change

Finding one hundred pentomino tilings on a 6x10 field

Library Baseline (ops/sec) PR (ops/sec) Change Performance
dancing-links (binary) 11.565 ±0.89% 11.039 ±2.08% -4.55% ⚠️ Minor regression
dancing-links (sparse) 11.645 ±1.12% 10.504 ±2.28% -9.80% ⚠️ Minor regression
dancing-links generator 11.952 ±0.66% 12.346 ±0.83% +3.29% ✅ Improvement
dancing-links template 11.925 ±0.76% 12.064 ±0.66% +1.16% ➡️ No significant change

Updated: 2026-07-13T23:00:15.035Z

node-22

dancing-links@4.3.7 compare-benchmarks
npm run build:dev && node built/scripts/compare-benchmarks.js baseline-results-node-22.json pr-results-node-22.json

dancing-links@4.3.7 build:dev
npm run clean && tsc -p tsconfig.dev.json

dancing-links@4.3.7 clean
rimraf built

🚀 Benchmark Results

All solutions to the sudoku

Library Baseline (ops/sec) PR (ops/sec) Change Performance
dancing-links (binary) 3,139.763 ±0.53% 3,632.44 ±0.30% +15.69% 🚀 Significant improvement
dancing-links (sparse) 11,000.057 ±0.36% 13,857.869 ±0.15% +25.98% 🚀 Significant improvement
dancing-links generator 11,598.509 ±0.28% 13,572.644 ±0.17% +17.02% 🚀 Significant improvement
dancing-links template 12,718.963 ±0.26% 14,871.988 ±0.15% +16.93% 🚀 Significant improvement

Finding one pentomino tiling on a 6x10 field

Library Baseline (ops/sec) PR (ops/sec) Change Performance
dancing-links (binary) 360.12 ±0.76% 401.942 ±0.78% +11.61% 🚀 Significant improvement
dancing-links (sparse) 384.838 ±0.78% 435.08 ±0.60% +13.06% 🚀 Significant improvement
dancing-links generator 394.298 ±0.72% 434.898 ±0.63% +10.30% 🚀 Significant improvement
dancing-links template 392.976 ±0.64% 442.681 ±0.60% +12.65% 🚀 Significant improvement

Finding ten pentomino tilings on a 6x10 field

Library Baseline (ops/sec) PR (ops/sec) Change Performance
dancing-links (binary) 59.837 ±1.32% 66.887 ±0.85% +11.78% 🚀 Significant improvement
dancing-links (sparse) 60.166 ±1.53% 67.379 ±1.34% +11.99% 🚀 Significant improvement
dancing-links generator 61.268 ±1.21% 67.494 ±1.43% +10.16% 🚀 Significant improvement
dancing-links template 60.989 ±2.24% 68.012 ±0.68% +11.52% 🚀 Significant improvement

Finding one hundred pentomino tilings on a 6x10 field

Library Baseline (ops/sec) PR (ops/sec) Change Performance
dancing-links (binary) 8.903 ±0.63% 8.588 ±3.01% -3.54% ⚠️ Minor regression
dancing-links (sparse) 8.877 ±0.67% 8.645 ±3.06% -2.61% ⚠️ Minor regression
dancing-links generator 9.609 ±1.17% 8.905 ±1.45% -7.33% ⚠️ Minor regression
dancing-links template 9.108 ±1.08% 9.058 ±1.62% -0.55% ➡️ No significant change

Updated: 2026-07-13T23:00:28.785Z

node-24

dancing-links@4.3.7 compare-benchmarks
npm run build:dev && node built/scripts/compare-benchmarks.js baseline-results-node-24.json pr-results-node-24.json

dancing-links@4.3.7 build:dev
npm run clean && tsc -p tsconfig.dev.json

dancing-links@4.3.7 clean
rimraf built

🚀 Benchmark Results

All solutions to the sudoku

Library Baseline (ops/sec) PR (ops/sec) Change Performance
dancing-links (binary) 5,172.047 ±0.36% 5,288.084 ±0.36% +2.24% ✅ Improvement
dancing-links (sparse) 11,792.336 ±0.27% 11,742.426 ±0.26% -0.42% ➡️ No significant change
dancing-links generator 11,209.768 ±0.31% 12,161.868 ±0.21% +8.49% ✅ Improvement
dancing-links template 13,050.077 ±0.27% 13,535.832 ±0.21% +3.72% ✅ Improvement

Finding one pentomino tiling on a 6x10 field

Library Baseline (ops/sec) PR (ops/sec) Change Performance
dancing-links (binary) 386.523 ±0.94% 392.744 ±0.76% +1.61% ➡️ No significant change
dancing-links (sparse) 407.612 ±0.84% 407.615 ±0.82% +0.00% ➡️ No significant change
dancing-links generator 423.594 ±0.75% 409.246 ±0.75% -3.39% ⚠️ Minor regression
dancing-links template 411.159 ±1.06% 420.278 ±0.68% +2.22% ✅ Improvement

Finding ten pentomino tilings on a 6x10 field

Library Baseline (ops/sec) PR (ops/sec) Change Performance
dancing-links (binary) 63.094 ±1.55% 64.124 ±1.54% +1.63% ➡️ No significant change
dancing-links (sparse) 63.602 ±1.44% 62.501 ±1.06% -1.73% ➡️ No significant change
dancing-links generator 63.999 ±1.65% 64.635 ±1.50% +0.99% ➡️ No significant change
dancing-links template 64.239 ±0.74% 65.066 ±1.45% +1.29% ➡️ No significant change

Finding one hundred pentomino tilings on a 6x10 field

Library Baseline (ops/sec) PR (ops/sec) Change Performance
dancing-links (binary) 9.187 ±0.97% 8.975 ±1.20% -2.31% ⚠️ Minor regression
dancing-links (sparse) 9.23 ±0.82% 8.968 ±1.06% -2.84% ⚠️ Minor regression
dancing-links generator 8.882 ±2.31% 7.5 ±2.71% -15.56% 🔴 Significant regression
dancing-links template 9.141 ±1.49% 8.151 ±3.39% -10.83% 🔴 Significant regression

Updated: 2026-07-13T23:00:32.351Z

node-25

dancing-links@4.3.7 compare-benchmarks
npm run build:dev && node built/scripts/compare-benchmarks.js baseline-results-node-25.json pr-results-node-25.json

dancing-links@4.3.7 build:dev
npm run clean && tsc -p tsconfig.dev.json

dancing-links@4.3.7 clean
rimraf built

🚀 Benchmark Results

All solutions to the sudoku

Library Baseline (ops/sec) PR (ops/sec) Change Performance
dancing-links (binary) 5,647.324 ±0.23% 5,665.338 ±0.26% +0.32% ➡️ No significant change
dancing-links (sparse) 12,354.469 ±0.21% 13,284.244 ±0.19% +7.53% ✅ Improvement
dancing-links generator 13,123.924 ±0.16% 13,486.694 ±0.16% +2.76% ✅ Improvement
dancing-links template 14,726.149 ±0.14% 14,970.307 ±0.14% +1.66% ➡️ No significant change

Finding one pentomino tiling on a 6x10 field

Library Baseline (ops/sec) PR (ops/sec) Change Performance
dancing-links (binary) 415.151 ±0.48% 417.597 ±0.59% +0.59% ➡️ No significant change
dancing-links (sparse) 433.221 ±0.49% 438.199 ±0.55% +1.15% ➡️ No significant change
dancing-links generator 431.365 ±0.60% 437.927 ±0.62% +1.52% ➡️ No significant change
dancing-links template 440.825 ±0.48% 437.164 ±0.69% -0.83% ➡️ No significant change

Finding ten pentomino tilings on a 6x10 field

Library Baseline (ops/sec) PR (ops/sec) Change Performance
dancing-links (binary) 59.518 ±1.43% 65.597 ±0.64% +10.21% 🚀 Significant improvement
dancing-links (sparse) 62.059 ±1.18% 66.602 ±1.27% +7.32% ✅ Improvement
dancing-links generator 60.122 ±1.86% 67.095 ±0.98% +11.60% 🚀 Significant improvement
dancing-links template 55.493 ±2.36% 66.241 ±1.46% +19.37% 🚀 Significant improvement

Finding one hundred pentomino tilings on a 6x10 field

Library Baseline (ops/sec) PR (ops/sec) Change Performance
dancing-links (binary) 9.225 ±0.74% 9.278 ±1.86% +0.58% ➡️ No significant change
dancing-links (sparse) 9.183 ±1.57% 9.538 ±0.66% +3.87% ✅ Improvement
dancing-links generator 9.284 ±1.28% 9.559 ±0.94% +2.97% ✅ Improvement
dancing-links template 9.315 ±0.80% 9.548 ±0.73% +2.50% ✅ Improvement

Updated: 2026-07-13T23:00:24.324Z

ℹ️ Benchmark Details
  • Baseline: Merge-base commit 513d1d5 where this PR branched from master
  • Comparison: Current PR head 99aa5c6
  • Timestamp: 2026-07-13 23:00:42 UTC

@TimBeyer TimBeyer marked this pull request as ready for review July 13, 2026 22:44
Generate release benchmark documentation on the existing Node 25 Namespace runner so published performance figures retain their consistent hardware baseline.

Transfer only README.md through a SHA-scoped, one-day artifact and apply it explicitly outside the checkout boundary before the GitHub-hosted OIDC release job.

Override release-it's benchmark hook only in CI, preventing an Ubuntu rerun while preserving the status-quo benchmark behavior for manual releases.
@TimBeyer TimBeyer merged commit 38ccda2 into master Jul 13, 2026
11 checks passed
@TimBeyer TimBeyer deleted the agent/npm-trusted-publishing branch July 13, 2026 22:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant