From 4693673e7f59a09ab8be9271c4d9783c148adbe0 Mon Sep 17 00:00:00 2001 From: markm39 Date: Fri, 14 Aug 2026 20:17:10 -0500 Subject: [PATCH] fix(release): let OIDC authenticate the npm publish The publish job authenticates with npm via OIDC trusted publishing and sets no NODE_AUTH_TOKEN, but it passed registry-url to setup-node. That makes setup-node write "//registry.npmjs.org/:_authToken=${NODE_AUTH_TOKEN}" into .npmrc, where the unset variable expands to an empty string. npm reads that as credentials already being configured, skips the OIDC token exchange, and publishes anonymously, which the registry rejects with E404. 0.3.3 built and packed correctly but never reached npm. Drop registry-url so the file is never written; npm already defaults to registry.npmjs.org. Pin the registry on the publish command instead, so the target stays explicit at the step that matters without configuring auth. Add a guard before publish that fails the job if any _authToken is configured, so this silently reverting to an anonymous publish becomes a loud failure at the exact assumption that broke. Refs https://github.com/npm/documentation/issues/1960 --- .github/workflows/publish.yml | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 5c3a0d9..6971a70 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -33,11 +33,17 @@ jobs: ref: ${{ github.event.workflow_run.head_sha }} persist-credentials: false + # registry-url is deliberately omitted. Setting it makes setup-node write + # "//registry.npmjs.org/:_authToken=${NODE_AUTH_TOKEN}" into .npmrc. This job has no + # NODE_AUTH_TOKEN because it authenticates via OIDC trusted publishing, so the + # placeholder would expand to an empty string, npm would read that as "credentials + # already configured", skip the OIDC token exchange, and publish anonymously. The + # registry rejects that with E404. npm defaults to https://registry.npmjs.org + # anyway, so the pin bought nothing. See https://github.com/npm/documentation/issues/1960 - name: Setup Node uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6 with: node-version: 24 - registry-url: https://registry.npmjs.org package-manager-cache: false - name: Install OIDC-capable npm CLI @@ -62,9 +68,23 @@ jobs: if: steps.registry.outputs.should_publish == 'true' run: npm run build + # Publishing here is authenticated by OIDC, not by a token, so npm must find no + # credential configured or it will skip the OIDC exchange entirely. Guard the + # invariant rather than trusting it: an anonymous publish fails at the registry + # with a bare E404 that points nowhere near the cause. + - name: Verify no npm credential is configured + if: steps.registry.outputs.should_publish == 'true' + run: | + set -euo pipefail + npmrc="${NPM_CONFIG_USERCONFIG:-$HOME/.npmrc}" + if [ -f "$npmrc" ] && grep -q '_authToken' "$npmrc"; then + echo "::error::$npmrc sets _authToken, so npm will skip the OIDC exchange and publish anonymously. Remove the credential (setup-node writes one when given registry-url)." + exit 1 + fi + - name: Publish package if: steps.registry.outputs.should_publish == 'true' - run: npm publish --access public --ignore-scripts + run: npm publish --access public --ignore-scripts --registry https://registry.npmjs.org - name: Create GitHub release env: