Skip to content

ci(npm): preflight Trusted Publishing before building or publishing - #6

Merged
ajay-bhargava merged 1 commit into
mainfrom
ci/npm-oidc-preflight
Jul 27, 2026
Merged

ci(npm): preflight Trusted Publishing before building or publishing#6
ajay-bhargava merged 1 commit into
mainfrom
ci/npm-oidc-preflight

Conversation

@ajay-bhargava

@ajay-bhargava ajay-bhargava commented Jul 27, 2026

Copy link
Copy Markdown
Member

Two release attempts (30310389376, 30311434844) both failed identically:

publishing @planmonster/olk-darwin-arm64@1.10.0-pm.2 ...
npm error 404 Not Found - PUT https://registry.npmjs.org/@planmonster%2folk-darwin-arm64

What the 404 actually means

From npm's lib/utils/oidc.js, Trusted Publishing is verified per package:

response = await npmFetch.json(
  new URL(`/-/npm/v1/oidc/token/exchange/package/${escapedPackageName}`, registry),
  { ...opts, [authTokenKey]: idToken, method: 'POST' })
} catch (error) {
  log.verbose('oidc', `Failed token exchange request ...`)
  return undefined        // <-- silent
}

On failure npm returns silently and falls back to the configured _authToken. actions/setup-node writes the placeholder XXXXX-XXXXX-XXXXX-XXXXX (confirmed in the run env: NODE_AUTH_TOKEN: XXXXX-XXXXX-XXXXX-XXXXX), so the registry rejects the PUT and masks 403 as 404. npm 11.16.0 and id-token: write were both fine — the exchange itself was refused for that package.

Three defects this exposed

  1. Undiagnosable. npm's explanation is at verbose level and we ran at default. Now sets NPM_CONFIG_LOGLEVEL: verbose on the publish step.
  2. Late, and capable of stranding a release. Packages publish one at a time, so package 4 of 7 failing would leave a half-published version — and npm versions are immutable, so it can never be repaired in place. New preflight job exchanges a token for all seven names before anything is built, printing exactly which are not ready plus the registry's own message:
  @planmonster/olkcli                READY
  @planmonster/olk-darwin-arm64      NOT READY  (http 404) <registry message>
  ...
preflight FAILED: 1 of 7 packages cannot publish via OIDC.

Builds now depend on it, so a misconfiguration costs ~20 s instead of ~6 min of Blacksmith + macOS time.
3. always-auth: true is invalid for actions/setup-node v6 (##[warning]Unexpected input(s) 'always-auth'). Removed.

Job graph

version -> preflight -> build-linux-windows ┐
                     -> build-darwin        ┴-> publish

preflight deliberately carries no job-level if — the build jobs depend on it, and a skipped dependency skips its dependents, which would have silently broken dry runs. The check is gated per step so a dry run leaves the job green.

Note

Nothing was published in either failed attempt — all seven packages still show only 1.10.0-pm.1, so 1.10.0-pm.2 is still free and no version was burned.


View with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is enabled.

Two consecutive release attempts failed with a bare:

  npm error 404 Not Found - PUT https://registry.npmjs.org/@planmonster%2folk-darwin-arm64

The 404 masks an authorization failure. From npm's lib/utils/oidc.js, Trusted
Publishing is checked *per package* by exchanging a GitHub OIDC token at
POST /-/npm/v1/oidc/token/exchange/package/<escaped-name>. A failed exchange is
swallowed — logged at verbose level, then `return undefined` — after which npm
falls back to whatever _authToken is configured. actions/setup-node writes the
placeholder XXXXX-XXXXX-XXXXX-XXXXX there, so the registry rejects the PUT and
the operator sees a 404 with no cause.

Three problems, all addressed:

1. The failure was undiagnosable. NPM_CONFIG_LOGLEVEL=verbose on the publish
   step surfaces npm's own OIDC message.

2. The failure came late and could leave a partial release. The packages publish
   one at a time, so package 4 of 7 failing would strand a version that is
   immutable and can never be reused. A new `preflight` job exchanges a token
   for all seven names up front and reports exactly which are not ready,
   including the registry's message and the exact fields to fix. The build jobs
   now depend on it, so a misconfiguration costs ~20 s instead of ~6 min of
   Blacksmith and macOS time.

3. `always-auth: true` is not a valid input for actions/setup-node v6 and was
   emitting "Unexpected input(s)". Removed.

The preflight job carries no job-level `if`: the build jobs depend on it, and a
skipped dependency skips its dependents, which would have broken dry runs. The
check is gated per step so a dry run leaves the job green.
@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 805b2a2f-bccd-4948-942c-f33dd12c82d0

📥 Commits

Reviewing files that changed from the base of the PR and between f3a6121 and 13cf80c.

📒 Files selected for processing (2)
  • .github/workflows/publish-npm.yml
  • scripts/preflight-npm-oidc.sh

📝 Walkthrough

Walkthrough

Changes

NPM OIDC publishing

Layer / File(s) Summary
OIDC exchange preflight
scripts/preflight-npm-oidc.sh
Requests a GitHub OIDC token, validates npm exchanges for seven packages, reports readiness, and exits unsuccessfully when checks fail.
Workflow gating and publish diagnostics
.github/workflows/publish-npm.yml
Adds the preflight job, gates builds and publishing on its result, and enables verbose npm logging for real publishes.

Sequence Diagram(s)

sequenceDiagram
  participant GitHubActions
  participant GitHubOIDC
  participant NPMRegistry
  participant BuildJobs
  participant PublishJob
  GitHubActions->>GitHubOIDC: Request ID token for npm audience
  GitHubActions->>NPMRegistry: Verify package token exchanges
  NPMRegistry-->>GitHubActions: Return readiness responses
  GitHubActions->>BuildJobs: Run builds after successful preflight
  BuildJobs-->>GitHubActions: Produce build artifacts
  GitHubActions->>PublishJob: Run publish after builds and preflight
  PublishJob->>NPMRegistry: Publish with verbose npm logging
Loading
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Comment @coderabbitai help to get the list of available commands.

@ajay-bhargava
ajay-bhargava merged commit 4855326 into main Jul 27, 2026
3 of 4 checks passed
@ajay-bhargava
ajay-bhargava deleted the ci/npm-oidc-preflight branch July 28, 2026 00:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant