Skip to content

Commit 2d3860d

Browse files
Elon Muskclaude
andauthored
ci(release): drop the inherited npm_config_globalconfig, so the nested pnpm can't hand npm a self-colliding config (#10148)
* ci(release): drop the inherited npm_config_globalconfig, so the nested pnpm can't hand npm a self-colliding config 17.5.0 failed to publish after a fully green build (run 32355381481) with an error carrying no npm error code at all: Received an unexpected error for @objectstack/account: (no code) Exit prior to config file resolving cause double-loading config "/home/runner/.config/pnpm/rc" as "global", previously loaded as "user" npm refuses to start when its "user" and "global" config resolve to the SAME file — it aborts inside @npmcli/config before it parses argv, which is why changesets could only report "(no code)". Three layers stack up to produce that one file twice: 1. `pnpm run release` exports npm_config_globalconfig=~/.config/pnpm/rc to every child; pnpm forces `{globalconfig: join(configDir, 'rc')}` into its rawConfig unconditionally. 2. `changeset publish` v3 detects the pnpm workspace and shells out to `pnpm info` / `pnpm publish` per package, where v2 always shelled out to `npm`. The publish therefore runs a NESTED pnpm underneath `pnpm run`. 3. That nested pnpm reads the inherited value back in and, delegating `info` to npm, hands the npm child BOTH npm_config_userconfig AND npm_config_globalconfig pointing at that file (pnpm/pnpm#10914, unfixed on the 10.31.0 line we pin). Only layer 2 is new, which is why 2026-08-14's release was green on the same pnpm pin: @changesets/cli@^2.31.1 spawned npm directly, so npm saw userconfig=$HOME/.npmrc against globalconfig=~/.config/pnpm/rc — two files, no collision. The bump to ^3.0.0 routed the publish through a nested pnpm. Dropping the inherited value is the whole fix. pnpm recomputes its own global config path from configDir either way, so nothing pnpm needs is lost, and npm falls back to its own default global config while keeping $HOME/.npmrc — where release.yml writes NPM_TOKEN — as the user config. `pnpm publish`'s npm child was already passed only globalconfig and never userconfig, so auth resolution is unchanged. Verified in the release lane's exact shape on the pinned pnpm 10.31.0 (`pnpm run` -> bash script -> nested pnpm): the negative control reproduces the CI error verbatim, and with the unset the nested call returns registry JSON. Forcing npm_config_userconfig=$HOME/.npmrc instead was tested and does NOT work — the nested pnpm recomputes it and overrides whatever we export. Fixes #10146 Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SRPcZzc7rfBQsNxJw3BSRh * docs(release): name the right version in the comment — the run failed 17.1.0, not 17.5.0 17.5.0 in that run's log is `@object-ui/console@17.5.0`, the vendored objectui build, not this repo's version. packages/cli/package.json at the run's SHA (47d1ae8, main's tip) declares 17.1.0, and the publish job's guard only proceeds when the dispatched input equals that string — so 17.1.0 is what failed, and 17.1.0 is what the re-dispatch publishes. npm still has latest 17.0.0 and 404s on 17.1.0, confirming the failed run wrote nothing. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SRPcZzc7rfBQsNxJw3BSRh --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 47d1ae8 commit 2d3860d

1 file changed

Lines changed: 33 additions & 0 deletions

File tree

scripts/release-publish.sh

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,39 @@
2727
# pushes tags itself.
2828
set -euo pipefail
2929

30+
# ⛔ Do not restore npm_config_globalconfig, and do not "simplify" this away.
31+
#
32+
# npm refuses to start when its "user" and its "global" config resolve to the
33+
# SAME file. It aborts inside @npmcli/config before it parses argv:
34+
#
35+
# Exit prior to config file resolving
36+
# cause
37+
# double-loading config "/home/runner/.config/pnpm/rc" as "global", previously loaded as "user"
38+
#
39+
# Three layers stack up to produce exactly that (#10146):
40+
# 1. `pnpm run release` exports npm_config_globalconfig=~/.config/pnpm/rc to
41+
# every child — pnpm forces `{globalconfig: join(configDir, 'rc')}` into
42+
# its rawConfig unconditionally.
43+
# 2. `changeset publish` v3 detects the pnpm workspace and shells out to
44+
# `pnpm info` / `pnpm publish` per package, where v2 always shelled out to
45+
# `npm`. The publish therefore runs a NESTED pnpm underneath `pnpm run`.
46+
# 3. That nested pnpm reads the inherited value back in and, delegating
47+
# `info` to npm, hands the npm child BOTH npm_config_userconfig AND
48+
# npm_config_globalconfig pointing at that one file (pnpm/pnpm#10914,
49+
# unfixed on the 10.31.0 line we pin).
50+
#
51+
# The abort happens before npm has an error code, so changesets can only
52+
# report `Received an unexpected error for <pkg>: (no code)` — which is how
53+
# run 32355381481 failed 17.1.0 after a fully green build.
54+
#
55+
# Dropping the inherited value is the whole fix: pnpm recomputes its own
56+
# global config path from configDir either way, so nothing pnpm needs is lost,
57+
# and npm falls back to its own default global config while keeping
58+
# $HOME/.npmrc — where release.yml writes NPM_TOKEN — as the user config.
59+
# Forcing npm_config_userconfig instead does NOT work: the nested pnpm
60+
# recomputes it and overrides whatever we export.
61+
unset npm_config_globalconfig NPM_CONFIG_GLOBALCONFIG
62+
3063
# Publish to npm and create the local version tags.
3164
changeset publish
3265

0 commit comments

Comments
 (0)