Skip to content

fix(ci): stop setup-node's .npmrc breaking every yarn step - #499

Merged
fjmorant merged 1 commit into
masterfrom
fix/publish-workflow-npmrc
Sep 26, 2026
Merged

fjmorant merged 1 commit into
masterfrom
fix/publish-workflow-npmrc

Conversation

@fjmorant

Copy link
Copy Markdown
Owner

My bug in #497. The Publish workflow failed after 8 seconds, and nothing was published — npm still shows 0.15.0 as latest, and no v1.0.0 tag or release exists.

error Error: Failed to replace env in config: ${NODE_AUTH_TOKEN}
    at envReplace (/usr/local/lib/node_modules/yarn/lib/cli.js:95448:16)

What happened

It failed inside setup-node itself, not in a step of mine. registry-url makes setup-node write an .npmrc containing the literal ${NODE_AUTH_TOKEN} for npm to expand at publish time. yarn 1 also reads .npmrc, and aborts on a variable that is not set — and cache: yarn on the same step shells out to yarn cache dir immediately after writing that file. The step killed itself before the workflow reached anything of its own.

Every yarn step after it would have failed identically, since NODE_AUTH_TOKEN was scoped to the publish step.

Reproduced locally with that exact .npmrc:

yarn cache dir, variable unset fails with the error above
yarn cache dir, variable defined passes
yarn cache dir, no .npmrc yet passes

The fix, and why not the one-liner

Defining NODE_AUTH_TOKEN at job level fixes it in one line. I did not do that: it would put the publish token in the environment of yarn install, yarn test and yarn build — the steps that run third-party code.

Instead the registry configuration moves to a second setup-node placed after the last yarn step, so the token stays scoped to the publish itself:

 2. setup-node (cache: yarn)     <-- runs yarn cache dir
 4. yarn install
 6..11. lint, type-check, test, build, check-build, check-package
12. setup-node (registry-url)    <-- writes .npmrc
13. Publish to npm

The invariant is nothing yarn-related runs after .npmrc exists. I verified the ordering holds after the change, and wrote the reason into the comment beside it, since appending a yarn step at the end would silently reintroduce the failure.

Also

Provenance is now left to real releases. A dry run has nothing to attest, so asking for an attestation of a publish that is not happening is just another way for the rehearsal to fail before it tells you anything useful.

After merging

  1. Re-run Publish from the Actions tab — it is the --dry-run rehearsal, and should now get all the way through to packing the tarball
  2. gh release create v1.0.0 --title 1.0.0

NPM_TOKEN still needs to be an npm automation token if your account requires 2FA to publish.

🤖 Generated with Claude Code

The Publish workflow failed after 8 seconds, inside setup-node itself:

  error Error: Failed to replace env in config: ${NODE_AUTH_TOKEN}
      at envReplace (/usr/local/lib/node_modules/yarn/lib/cli.js:95448:16)

registry-url makes setup-node write an .npmrc containing the literal
${NODE_AUTH_TOKEN}, which npm expands at publish time. yarn 1 reads .npmrc too
and aborts on a variable that is not set, and cache: yarn on the same step
shells out to `yarn cache dir` immediately after writing that file — so the
step killed itself before the workflow reached anything of its own. Every yarn
step after it would have failed the same way, since NODE_AUTH_TOKEN was scoped
to the publish step.

Reproduced locally with that .npmrc and `yarn cache dir`: it fails with the
variable unset, and passes both when the variable is defined and when the file
does not yet exist.

Defining NODE_AUTH_TOKEN for the whole job would fix it in one line, but it
would also put the publish token in the environment of yarn install, test and
build — the steps that run third-party code. So the registry configuration
moves to a second setup-node placed after the last yarn step instead, which
keeps the token scoped to the publish itself. A check on the step ordering is
written into the comment that explains it, since adding a yarn step at the end
would silently reintroduce the failure.

Provenance is now also left to real releases. A dry run has nothing to attest,
and asking for an attestation of a publish that is not happening is a
needless way for the rehearsal to fail.

Nothing was published: the failure was well before npm ran, and npm still shows
0.15.0 as latest.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@fjmorant
fjmorant merged commit 80c5018 into master Sep 26, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant