Skip to content

Commit fb1ffdd

Browse files
ralyodioclaude
andcommitted
ci: publish with NPM_TOKEN again
Two OIDC attempts, two failures with npm reporting no credential at all. The second one was worth it — it found a real bug in this file, where setup-node's `registry-url` left an empty auth token that stopped npm attempting the exchange — but fixing that only moved the error from E404 to ENEEDAUTH. The npmjs.com registration remains unconfirmed, and it is not something this repository can check or set. So: back to the path that demonstrably works. v0.24.2 published this way, with a provenance attestation. Carrying forward what the attempts taught, in the header and at the step: `registry-url` is required for token auth and fatal for OIDC. That asymmetry is the whole trap, and it is now written down next to the line it applies to. Keeps node 24 from #311 — unrelated to auth, and the tests pass on it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent 7597d40 commit fb1ffdd

1 file changed

Lines changed: 37 additions & 56 deletions

File tree

.github/workflows/publish.yml

Lines changed: 37 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -10,31 +10,25 @@
1010
# that failed on something transient; it is safe because a version already on
1111
# the registry is skipped rather than attempted.
1212
#
13-
# Authenticates by trusted publishing (OIDC) rather than a stored token: npm
14-
# trades the short-lived token GitHub mints for this specific workflow run for
15-
# permission to publish, so there is no long-lived credential in the repository
16-
# to leak, rotate or forget.
13+
# Authenticates with a stored npm automation token, in the repository secret
14+
# `NPM_TOKEN`. Trusted publishing (OIDC) would avoid the stored credential and
15+
# was tried twice; both attempts failed with npm reporting no credential at all,
16+
# and the registration on npmjs.com — the web-UI half of it, pinned to this
17+
# file's name — is what remains unconfirmed. See #305, #309, #311.
1718
#
18-
# The other half of that trust lives on npmjs.com, under the package's Trusted
19-
# Publisher settings, and it is pinned to the *filename* of this workflow.
20-
# Renaming this file silently breaks publishing — npm will refuse the exchange
21-
# because the run no longer matches what was configured.
19+
# If you go back to OIDC, the trap is below: `registry-url` is *required* here
20+
# and *fatal* there. setup-node writes
2221
#
23-
# When it goes wrong the error names neither OIDC nor trusted publishing:
22+
# //registry.npmjs.org/:_authToken=${NODE_AUTH_TOKEN}
2423
#
25-
# npm error code E404
26-
# npm error 404 Not Found - PUT https://registry.npmjs.org/moshcode
27-
# npm error 404 ... could not be found or you do not have permission
24+
# which is how the token reaches npm — and under OIDC, with no token to fill it,
25+
# resolves to an empty credential that stops npm attempting the exchange.
26+
# Removing it changed the error from E404 to ENEEDAUTH but did not publish.
27+
# actions/setup-node#1551, npm/cli#9088.
2828
#
29-
# That is an unauthenticated PUT, reported as though the package did not exist.
30-
# It has two quite different causes, and it took v0.24.3 to tell them apart:
31-
#
32-
# 1. anything that leaves an empty auth token in an .npmrc, which stops npm
33-
# attempting the exchange at all — see the setup-node note below; or
34-
# 2. no trusted publisher registered for this package, or one registered
35-
# against a different workflow filename.
36-
#
37-
# Check 1 first. It is in this file, and it is the one that looks like 2.
29+
# Worth knowing when this is next revisited: npm is restricting tokens that
30+
# bypass 2FA for direct publishing, so the token path has a horizon.
31+
# https://gh.io/npm-gat-bypass2fa-deprecation
3832
name: publish
3933

4034
on:
@@ -44,9 +38,9 @@ on:
4438

4539
permissions:
4640
contents: read
47-
# The whole basis of the exchange: this is what lets the run mint the OIDC
48-
# token npm authenticates against. Without it there is no credential at all
49-
# and publishing fails outright.
41+
# Still needed with token auth: provenance is signed with a short-lived OIDC
42+
# token even though the publish itself authenticates with NPM_TOKEN. Without
43+
# it `--provenance` fails.
5044
id-token: write
5145

5246
jobs:
@@ -61,38 +55,14 @@ jobs:
6155
# with ERR_PNPM_BAD_PM_VERSION.
6256
- uses: pnpm/action-setup@v4
6357

64-
# Deliberately no `registry-url`. With it, setup-node always writes
65-
#
66-
# //registry.npmjs.org/:_authToken=${NODE_AUTH_TOKEN}
67-
#
68-
# into an .npmrc — correct for token auth, and quietly fatal here. Under
69-
# trusted publishing there is no NODE_AUTH_TOKEN, so that line resolves to
70-
# an empty token, and npm stops before ever attempting the OIDC exchange:
71-
# it believes it already has credentials. The registry then answers the
72-
# unauthenticated PUT with E404, which names nothing to do with OIDC and
73-
# is what sent us looking at the npmjs.com config instead of at this file.
74-
#
75-
# actions/setup-node#1551. Without registry-url no .npmrc is written and
76-
# npm defaults to registry.npmjs.org anyway, which is where we publish.
58+
# `registry-url` is what makes setup-node write the .npmrc line that feeds
59+
# NODE_AUTH_TOKEN to npm. Required for token auth — and the thing to delete
60+
# first if this ever moves back to OIDC.
7761
- uses: actions/setup-node@v4
7862
with:
7963
node-version: 24
8064
cache: pnpm
81-
82-
# Node 24 bundles npm 11, but pin the floor anyway rather than depend on
83-
# what a runner image happens to ship: below 11.5.1 there is no trusted
84-
# publishing, and the failure would again look like a credential problem.
85-
- name: Install an npm that understands trusted publishing
86-
run: |
87-
npm install -g npm@latest
88-
VERSION="$(npm --version)"
89-
MINIMUM=11.5.1
90-
echo "npm $VERSION"
91-
# Lowest of the two must be the minimum, or this npm is older than it.
92-
if [ "$(printf '%s\n%s\n' "$MINIMUM" "$VERSION" | sort -V | head -n1)" != "$MINIMUM" ]; then
93-
echo "::error::npm $VERSION cannot use trusted publishing — $MINIMUM or later is required"
94-
exit 1
95-
fi
65+
registry-url: https://registry.npmjs.org
9666

9767
- run: pnpm install --frozen-lockfile
9868

@@ -138,13 +108,24 @@ jobs:
138108
echo "already=false" >> "$GITHUB_OUTPUT"
139109
fi
140110
141-
# No token. `--provenance` is passed even though npm documents it as
142-
# automatic under trusted publishing: reports differ on whether it really
143-
# is, and asking for it explicitly costs nothing and cannot produce a
144-
# weaker result. v0.24.2 published with an attestation using this flag.
111+
# Said plainly here, rather than as the E404/no-permission npm otherwise
112+
# returns partway through a release — an error that reads as "the package
113+
# does not exist" rather than "there is no credential".
114+
- name: Require an npm token
115+
if: steps.published.outputs.already == 'false'
116+
env:
117+
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
118+
run: |
119+
if [ -z "$NPM_TOKEN" ]; then
120+
echo "::error::NPM_TOKEN is not set — add an npm automation token as a repository secret named NPM_TOKEN"
121+
exit 1
122+
fi
123+
145124
- name: Publish
146125
if: steps.published.outputs.already == 'false'
147126
run: npm publish --access public --provenance
127+
env:
128+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
148129

149130
- name: Confirm the registry has it
150131
if: steps.published.outputs.already == 'false'

0 commit comments

Comments
 (0)