Skip to content

Commit 3898396

Browse files
yanxue06cursoragent
andcommitted
fix(publish-npm): verify version landed on registry to catch silent failures
The publish-npm block currently trusts the publisher CLI's exit code, but `bunx clean-publish` (and `bunx`-wrapped commands generally) can swallow `npm publish`'s non-zero exit. The OIDC branch additionally catches its own publish failures to fall through to the token path. Together, these mean a job can finish green with nothing on npm — which is exactly what happened to spectrum-ts 1.10.0..1.11.1 (four silent-fail releases under an expired NPM_TOKEN). Add a final assertion that probes registry.npmjs.org for the just- published <name>@<version> (resolved from package.json, the source of truth both publish paths consume). Six retries spaced 5s apart absorb npm CDN propagation. Runs only on real publishes (not --dry-run). On failure, prints the most likely root causes — token revoked, scope narrowed, OIDC misconfigured, or publisher CLI swallowed an error — so operators don't have to re-derive the diagnosis each time. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 15ee35b commit 3898396

1 file changed

Lines changed: 49 additions & 0 deletions

File tree

.github/blocks/publish-npm/action.yaml

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,3 +122,52 @@ runs:
122122
123123
echo "🔑 Publishing via NPM token..."
124124
${{ inputs.publish-command }} --tag ${{ inputs.tag }} --access public
125+
126+
# Step 3: verify the version actually landed on the npm registry.
127+
#
128+
# Why this exists: publishers like `bunx clean-publish` (and `bunx`-wrapped
129+
# commands generally) can swallow `npm publish`'s non-zero exit code, so a
130+
# 404/auth failure prints `npm error code E404` to the log but leaves the
131+
# step exit code at 0. Without this assertion, the whole release pipeline
132+
# goes green while npm has nothing — which is exactly what happened to
133+
# spectrum-ts 1.10.0..1.11.1 (silent fails for four releases). The OIDC
134+
# branch above also explicitly catches publish failures to fall through to
135+
# the token path, so neither path can be trusted on its own.
136+
#
137+
# We resolve <name>@<version> from package.json (the source of truth that
138+
# both publish paths consume) and probe the registry for that exact tarball.
139+
# A few retries absorb npm CDN propagation, which is typically <10s.
140+
- name: Verify publish landed on registry
141+
if: ${{ inputs.dry-run != 'true' }}
142+
shell: bash
143+
working-directory: ${{ inputs.working-directory }}
144+
run: |
145+
NAME=$(jq -r .name package.json)
146+
VERSION=$(jq -r .version package.json)
147+
if [ -z "$NAME" ] || [ "$NAME" = "null" ] || [ -z "$VERSION" ] || [ "$VERSION" = "null" ]; then
148+
echo "❌ Could not read name/version from package.json; cannot verify publish."
149+
exit 1
150+
fi
151+
152+
URL="https://registry.npmjs.org/$NAME/$VERSION"
153+
echo "🔎 Verifying $NAME@$VERSION at $URL ..."
154+
155+
for attempt in 1 2 3 4 5 6; do
156+
STATUS=$(curl -fsS -o /dev/null -w "%{http_code}" "$URL" || true)
157+
if [ "$STATUS" = "200" ]; then
158+
echo "✅ $NAME@$VERSION is live on the npm registry."
159+
exit 0
160+
fi
161+
echo "Attempt $attempt: registry returned HTTP $STATUS — retrying in 5s..."
162+
sleep 5
163+
done
164+
165+
echo "❌ Publish verification failed: $NAME@$VERSION is not on the registry."
166+
echo ""
167+
echo "The publisher reported success but the version did not land on npm."
168+
echo "Common causes:"
169+
echo " • NPM_TOKEN expired/revoked (npm returns 404, not 401/403, for auth failures)"
170+
echo " • NPM_TOKEN scope does not include write access to this package"
171+
echo " • OIDC Trusted Publisher not configured for this repo+workflow on npmjs.com"
172+
echo " • Publisher CLI (e.g. 'bunx clean-publish') swallowed npm's non-zero exit"
173+
exit 1

0 commit comments

Comments
 (0)