From 02798b460e05b000efba7149d692ecff7edce388 Mon Sep 17 00:00:00 2001 From: os-elon Date: Thu, 20 Aug 2026 10:18:31 +0000 Subject: [PATCH 1/2] ci(release): drop the inherited npm_config_globalconfig, so the nested pnpm can't hand npm a self-colliding config MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Claude-Session: https://claude.ai/code/session_01SRPcZzc7rfBQsNxJw3BSRh --- scripts/release-publish.sh | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/scripts/release-publish.sh b/scripts/release-publish.sh index 8d881a5051..3db42efd79 100755 --- a/scripts/release-publish.sh +++ b/scripts/release-publish.sh @@ -27,6 +27,39 @@ # pushes tags itself. set -euo pipefail +# ⛔ Do not restore npm_config_globalconfig, and do not "simplify" this away. +# +# npm refuses to start when its "user" and its "global" config resolve to the +# SAME file. It aborts inside @npmcli/config before it parses argv: +# +# Exit prior to config file resolving +# cause +# double-loading config "/home/runner/.config/pnpm/rc" as "global", previously loaded as "user" +# +# Three layers stack up to produce exactly that (#10146): +# 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 one file (pnpm/pnpm#10914, +# unfixed on the 10.31.0 line we pin). +# +# The abort happens before npm has an error code, so changesets can only +# report `Received an unexpected error for : (no code)` — which is how +# run 32355381481 failed 17.5.0 after a fully green build. +# +# 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. +# Forcing npm_config_userconfig instead does NOT work: the nested pnpm +# recomputes it and overrides whatever we export. +unset npm_config_globalconfig NPM_CONFIG_GLOBALCONFIG + # Publish to npm and create the local version tags. changeset publish From 4f03db9d6c927d284af02aaf2ba2e9f298754ce8 Mon Sep 17 00:00:00 2001 From: os-elon Date: Thu, 20 Aug 2026 10:49:41 +0000 Subject: [PATCH 2/2] =?UTF-8?q?docs(release):=20name=20the=20right=20versi?= =?UTF-8?q?on=20in=20the=20comment=20=E2=80=94=20the=20run=20failed=2017.1?= =?UTF-8?q?.0,=20not=2017.5.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Claude-Session: https://claude.ai/code/session_01SRPcZzc7rfBQsNxJw3BSRh --- scripts/release-publish.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/release-publish.sh b/scripts/release-publish.sh index 3db42efd79..f353f09303 100755 --- a/scripts/release-publish.sh +++ b/scripts/release-publish.sh @@ -50,7 +50,7 @@ set -euo pipefail # # The abort happens before npm has an error code, so changesets can only # report `Received an unexpected error for : (no code)` — which is how -# run 32355381481 failed 17.5.0 after a fully green build. +# run 32355381481 failed 17.1.0 after a fully green build. # # 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,