Skip to content

Commit afd574d

Browse files
ralyodioclaude
andcommitted
ci: publish to npm by trusted publishing instead of a stored token
Removes the NPM_TOKEN requirement. npm now trades the short-lived OIDC token GitHub mints for this specific workflow run for permission to publish, so there is no long-lived credential in the repository to leak, rotate or forget — and none had to be minted, since the secret was never added. Two things that would have failed quietly otherwise: Node 22 bundles npm 10, which predates trusted publishing and falls back to looking for a token that no longer exists — an auth failure that reads as a credential problem rather than a version one. So npm is upgraded first, and the 11.5.1 floor is checked rather than assumed. The check compares with `sort -V`: lexically, 11.16.0 is lower than 11.5.1. `--provenance` is gone because trusted publishing generates and attaches the attestation itself. The other half of the trust lives on npmjs.com under the package's Trusted Publisher settings, and it is pinned to this file's *name*. Renaming publish.yml breaks publishing with no other symptom, which the header now says. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent a0d3145 commit afd574d

1 file changed

Lines changed: 31 additions & 20 deletions

File tree

.github/workflows/publish.yml

Lines changed: 31 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,15 @@
1010
# that failed on something transient; it is safe because a version already on
1111
# the registry is skipped rather than attempted.
1212
#
13-
# Requires a repository secret `NPM_TOKEN` — an npm automation token, which is
14-
# the kind that publishes without a 2FA prompt.
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.
17+
#
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.
1522
name: publish
1623

1724
on:
@@ -21,9 +28,9 @@ on:
2128

2229
permissions:
2330
contents: read
24-
# For npm provenance: the attestation is signed with a short-lived OIDC token
25-
# rather than anything stored here, and it is what lets npm show which build
26-
# this tarball actually came from.
31+
# The whole basis of the exchange: this is what lets the run mint the OIDC
32+
# token npm authenticates against. Without it there is no credential at all
33+
# and publishing fails outright.
2734
id-token: write
2835

2936
jobs:
@@ -44,6 +51,22 @@ jobs:
4451
cache: pnpm
4552
registry-url: https://registry.npmjs.org
4653

54+
# Node 22 bundles npm 10, which predates trusted publishing and would fall
55+
# back to looking for a token that no longer exists — an auth failure that
56+
# reads as a credential problem rather than a version one. 11.5.1 is the
57+
# floor; the check below says so plainly if that ever regresses.
58+
- name: Install an npm that understands trusted publishing
59+
run: |
60+
npm install -g npm@latest
61+
VERSION="$(npm --version)"
62+
MINIMUM=11.5.1
63+
echo "npm $VERSION"
64+
# Lowest of the two must be the minimum, or this npm is older than it.
65+
if [ "$(printf '%s\n%s\n' "$MINIMUM" "$VERSION" | sort -V | head -n1)" != "$MINIMUM" ]; then
66+
echo "::error::npm $VERSION cannot use trusted publishing — $MINIMUM or later is required"
67+
exit 1
68+
fi
69+
4770
- run: pnpm install --frozen-lockfile
4871

4972
# Publishing is the one action here that cannot be taken back — npm will
@@ -88,23 +111,11 @@ jobs:
88111
echo "already=false" >> "$GITHUB_OUTPUT"
89112
fi
90113
91-
# Said plainly here, rather than as the 401 npm would otherwise return
92-
# partway through a release.
93-
- name: Require an npm token
94-
if: steps.published.outputs.already == 'false'
95-
env:
96-
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
97-
run: |
98-
if [ -z "$NPM_TOKEN" ]; then
99-
echo "::error::NPM_TOKEN is not set — add an npm automation token as a repository secret named NPM_TOKEN"
100-
exit 1
101-
fi
102-
114+
# No token, and no `--provenance` either: publishing through trusted
115+
# publishing generates and attaches the attestation on its own.
103116
- name: Publish
104117
if: steps.published.outputs.already == 'false'
105-
run: npm publish --access public --provenance
106-
env:
107-
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
118+
run: npm publish --access public
108119

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

0 commit comments

Comments
 (0)