From 2370620edef94f655c0f442ecc2f1ff0ed22ed36 Mon Sep 17 00:00:00 2001 From: Tim Beyer Date: Tue, 14 Jul 2026 00:39:23 +0200 Subject: [PATCH 1/2] fix(ci): publish npm releases through trusted OIDC 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/workflows/release.yml | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b935a90..a70a4fd 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -28,22 +28,31 @@ jobs: # Release only after all prerequisites complete release: - runs-on: namespace-profile-default + # npm trusted publishing currently accepts only GitHub-hosted runners. + runs-on: ubuntu-latest needs: [lint, test, build] + permissions: + contents: write + id-token: write steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: fetch-depth: 0 token: ${{ secrets.GITHUB_TOKEN }} - name: Setup Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@v6 with: - node-version: 25 - cache: 'npm' + node-version: 24 registry-url: 'https://registry.npmjs.org' + package-manager-cache: false + + - name: Install npm with trusted publishing support + # Trusted publishing requires npm >=11.5.1; pin the major so a future + # npm release cannot silently change the privileged release toolchain. + run: npm install --global npm@11 - name: Install dependencies run: npm ci @@ -57,8 +66,8 @@ jobs: git config --global user.email "github-actions[bot]@users.noreply.github.com" - name: Release - run: npm run release -- --ci + # release-it skips its token-only npm preflight; npm publish exchanges + # this job's short-lived OIDC identity for registry authorization. + run: npm run release -- --ci --npm.skipChecks env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} From 513d1d5bd77872608c10ebdb8467a1c4b4d3c2fb Mon Sep 17 00:00:00 2001 From: Tim Beyer Date: Tue, 14 Jul 2026 00:57:30 +0200 Subject: [PATCH 2/2] fix(ci): benchmark releases on Namespace 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. --- .github/workflows/release.yml | 59 ++++++++++++++++++++++++++++++++--- 1 file changed, 55 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a70a4fd..1c00395 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -26,11 +26,46 @@ jobs: needs: should-run uses: ./.github/workflows/build.yml + # Generate release benchmark documentation on consistent Namespace hardware. + # GitHub-hosted CPU performance is too variable for published comparisons. + benchmark-docs: + runs-on: namespace-profile-default + needs: [lint, test, build] + permissions: + contents: read + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + # Preserve the runtime used by the release benchmarks before OIDC + # publishing required the release job itself to use Node.js 24. + node-version: 25 + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Update benchmark documentation + run: npm run update-benchmark-docs -- --quiet + + - name: Upload benchmark documentation + # Pass only non-executable benchmark output into the privileged job. + uses: actions/upload-artifact@v4 + with: + name: release-benchmark-docs-${{ github.sha }} + path: README.md + if-no-files-found: error + retention-days: 1 + # Release only after all prerequisites complete release: # npm trusted publishing currently accepts only GitHub-hosted runners. runs-on: ubuntu-latest - needs: [lint, test, build] + needs: benchmark-docs permissions: contents: write id-token: write @@ -42,6 +77,21 @@ jobs: fetch-depth: 0 token: ${{ secrets.GITHUB_TOKEN }} + - name: Download benchmark documentation + # Extract outside the checkout so the artifact cannot add package files. + uses: actions/download-artifact@v4 + with: + name: release-benchmark-docs-${{ github.sha }} + path: ${{ runner.temp }}/release-benchmark-docs + + - name: Apply benchmark documentation + run: cp "$RUNNER_TEMP/release-benchmark-docs/README.md" README.md + + - name: Verify benchmark documentation scope + # The trusted job consumes the Namespace result only as README data; + # executable release inputs must still come from the checked-out SHA. + run: git diff --exit-code -- . ':(exclude)README.md' + - name: Setup Node.js uses: actions/setup-node@v6 with: @@ -66,8 +116,9 @@ jobs: git config --global user.email "github-actions[bot]@users.noreply.github.com" - name: Release - # release-it skips its token-only npm preflight; npm publish exchanges - # this job's short-lived OIDC identity for registry authorization. - run: npm run release -- --ci --npm.skipChecks + # Skip release-it's token-only npm preflight and override only its + # benchmark hook: Namespace already generated the README, so the + # variable GitHub-hosted CPU must not replace those measurements. + run: npm run release -- --ci --npm.skipChecks --hooks.after:bump="npm run format" env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}