install: fall back to $HOME/.npmrc when $XDG_CONFIG_HOME is set
Merged
RO
3mo ago+89+89+01234567890123456789-23-23-01234567890123456789
Fixes #24124. Fixes #22971. Related: #23128 (the .npmrc half), supersedes the closed #26040.
Problem
- With
XDG_CONFIG_HOMEexported (GitHub Actions ubuntu-latest sets it to~/.config; many Linux desktops set it too),bun publishfails witherror: missing authentication (runbunx npm login)even though~/.npmrcholds a valid token andnpm whoamisucceeds.bun installlikewise ignores a registry configured in~/.npmrc. - Cause:
PackageManager::init(src/install/PackageManager.rs, theinitialize_store()block) picked the user-level.npmrcdirectory withXDG_CONFIG_HOME.get().or_else(|| HOME.get()), so onceXDG_CONFIG_HOMEwas set,$HOME/.npmrcwas never read, whether or not$XDG_CONFIG_HOME/.npmrcexisted. npm loginwrites$HOME/.npmrc; npm reads that file and ignoresXDG_CONFIG_HOME.- Not covered here:
actions/setup-nodewrites$RUNNER_TEMP/.npmrcand pointsNPM_CONFIG_USERCONFIGat it. bun does not readNPM_CONFIG_USERCONFIG, so that setup (#14824) still fails after this change.
Fix
- Use
$XDG_CONFIG_HOME/.npmrcwhen that file exists (keeps working for users who already put it there), otherwise$HOME/.npmrc. An emptyXDG_CONFIG_HOMEorHOMEis treated as unset (get_not_empty()). The project-local.npmrcis still loaded afterwards and still takes precedence, as before. - Tests:
test/cli/install/npmrc.test.ts,describe("user .npmrc lookup"), four cases driven throughbun publish --dry-run(needs the token, never contacts a registry; theRegistry:line in its output shows which file was read):$XDG_CONFIG_HOME/.npmrcpresent: it winsXDG_CONFIG_HOMEset, no file there:$HOME/.npmrcis used (fails on main)XDG_CONFIG_HOMEunset:$HOME/.npmrcis usedXDG_CONFIG_HOME="":$HOME/.npmrcis used (fails on main)
USE_SYSTEM_BUN=1 bun test test/cli/install/npmrc.test.ts -t "user .npmrc lookup": 2 pass, 2 fail (missing authentication)bun bd test test/cli/install/npmrc.test.ts: 31 pass (whole file)
Background
.npmrcis npm's ini-style config file (registry URL,//host/:_authToken=credentials, and so on). Bun reads up to two of them on every install-family command: one user-level file and the project's./.npmrc, later files overriding earlier ones (ini::load_npmrc_config).XDG_CONFIG_HOMEis the freedesktop variable for a user's config directory, usually~/.config. Bun has historically also looked for.npmrcdirectly inside it; npm does not.
[review] gate passed · iteration 6 · 2 files touched
fails on main (without fix)
ASAN without fix: 2 FAILED
$ BUN_DEBUG_QUIET_LOGS=1 bun scripts/build.ts --profile=debug --quiet test "--reporter=junit" "--reporter-outfile=/tmp/mechgate.xml" test/cli/install/npmrc.test.ts
bun test v1.4.0 (930b7af16)
test/cli/install/npmrc.test.ts:
{
out: "/tmp/verdaccio-test-_Fyh66Z/hi!",
err: "",
}
(pass) npmrc > should convert to utf8 if BOM [348.58ms]
package dir /tmp/verdaccio-test-_IeyYIw
bun install v1.4.0 (930b7af16)
No packages! Deleted empty lockfile
[108.00ms] done
(pass) npmrc > works with empty file [281.54ms]
package dir /tmp/verdaccio-test-_9ODdIL
bun install v1.4.0 (930b7af16)
Resolving dependencies
Resolved, downloaded and extracted [2]
Saved lockfile
+ no-deps@1.0.0 (v2.0.0 available)
1 package installed [411.00ms]
(pass) npmrc > sets default registry [536.32ms]
bun install v1.4.0 (930b7af16)
Resolving dependencies
Resolved, downloaded and extracted [2]
Saved lockfile
+ @types/no-deps@1.0.0 (v2.0.0 available)
1 package installed [570.00ms]
(pass) npmrc > sets scoped registry [727.22ms]
package dir /tmp/verdaccio-test-_Y7OsCk
home dir /tmp/verdaccio-test-_Y7OsCk/home_dir
bun install v1.4.0 (930b7af16)
Saved lockfile
+ no-deps@1.0.0 (v2.0.0 available)
1 package i
... (truncated)
release without fix: 2 FAILED
bun test v1.4.0-canary.1 (da3851e57)
test/cli/install/npmrc.test.ts:
{
out: "/tmp/verdaccio-test-_L3vvhh/hi!",
err: "",
}
(pass) npmrc > should convert to utf8 if BOM [6.60ms]
package dir /tmp/verdaccio-test-_WPggNn
bun install v1.4.0-canary.1 (da3851e57)
No packages! Deleted empty lockfile
[1.00ms] done
(pass) npmrc > works with empty file [8.16ms]
package dir /tmp/verdaccio-test-_I9szLQ
bun install v1.4.0-canary.1 (da3851e57)
Resolving dependencies
Resolved, downloaded and extracted [2]
Saved lockfile
+ no-deps@1.0.0 (v2.0.0 available)
1 package installed [28.00ms]
(pass) npmrc > sets default registry [48.01ms]
bun install v1.4.0-canary.1 (da3851e57)
Resolving dependencies
Resolved, downloaded and extracted [2]
Saved lockfile
+ @types/no-deps@1.0.0 (v2.0.0 available)
1 package installed [314.00ms]
(pass) npmrc > sets scoped registry [333.60ms]
package dir /tmp/verdaccio-test-_ck6dGm
home dir /tmp/verdaccio-test-_ck6dGm/home_dir
bun install v1.4.0-canary.1 (da3851e57)
Saved lockfile
+ no-deps@1.0.0 (v2.0.0 available)
1 package installed [169.00ms]
(pass) npmrc > works with home config [296.00ms]
package dir /tmp/verdaccio-test-_5UnG2b
home dir /tmp/verd
... (truncated)
passes on PR (with fix)
ASAN with fix: all passed
$ BUN_DEBUG_QUIET_LOGS=1 bun scripts/build.ts --profile=debug --quiet test "--reporter=junit" "--reporter-outfile=/tmp/mechgate.xml" test/cli/install/npmrc.test.ts
bun test v1.4.0 (930b7af16)
test/cli/install/npmrc.test.ts:
{
out: "/tmp/verdaccio-test-_SV2m9B/hi!",
err: "",
}
(pass) npmrc > should convert to utf8 if BOM [342.55ms]
package dir /tmp/verdaccio-test-_YWNf8S
bun install v1.4.0 (930b7af16)
No packages! Deleted empty lockfile
[1184.00ms] done
(pass) npmrc > works with empty file [1519.19ms]
package dir /tmp/verdaccio-test-_CGgs8v
bun install v1.4.0 (930b7af16)
Resolving dependencies
Resolved, downloaded and extracted [2]
Saved lockfile
+ no-deps@1.0.0 (v2.0.0 available)
1 package installed [1092.00ms]
(pass) npmrc > sets default registry [1273.03ms]
bun install v1.4.0 (930b7af16)
Resolving dependencies
Resolved, downloaded and extracted [2]
Saved lockfile
+ @types/no-deps@1.0.0 (v2.0.0 available)
1 package installed [418.00ms]
(pass) npmrc > sets scoped registry [586.55ms]
package dir /tmp/verdaccio-test-_ZePx90
home dir /tmp/verdaccio-test-_ZePx90/home_dir
bun install v1.4.0 (930b7af16)
Saved lockfile
+ no-deps@1.0.0 (v2.0.0 available)
1 packa
... (truncated)
release with fix: all passed
$ bun scripts/build.ts --profile=release
[configured] bun-profile → bun (stripped) in 1126ms (unchanged)
ninja: Entering directory `/workspace/bun/build/release'
[1/252] gen generated_host_exports.rs
generated_host_exports.rs: 93 exports (host=3, lazy=10, generic=80, rust=0); 239 extern-C blocks audited
[1/252] cargo bun_bin → libbun_rust.a (--target x86_64-unknown-linux-gnu)
nightly-2026-07-20-x86_64-unknown-linux-gnu unchanged - rustc 1.99.0-nightly (9f36de775 2026-07-19)
^[[1m^[[92m Compiling^[[0m bun_io v0.0.0 (/workspace/bun/src/io)
^[[1m^[[92m Compiling^[[0m bun_parsers v0.0.0 (/workspace/bun/src/parsers)
^[[1m^[[92m Compiling^[[0m bun_react_compiler v0.0.0 (/workspace/bun/src/react_compiler)
^[[1m^[[92m Compiling^[[0m bun_zlib v0.0.0 (/workspace/bun/src/zlib)
^[[1m^[[92m Compiling^[[0m bun_event_loop v0.0.0 (/workspace/bun/src/event_loop)
^[[1m^[[92m Compiling^[[0m bun_sourcemap v0.0.0 (/workspace/bun/src/sourcemap)
^[[1m^[[92m Compiling^[[0m bun_css v0.0.0 (/workspace/bun/src/css)
^[[1m^[[92m Compiling^[[0m bun_options_types v0.0.0 (/workspace/bun/src/options_types)
^[[1m^[[92m Compiling^[[0m bun_http v0.0.0 (/workspace/bun/src/http)
^[[1m^[[92m Compiling^[[0m
... (truncated)
diff hotspot
src/install/PackageManager.rs | 56 +++++++++++++++++++++++++-----------------
test/cli/install/npmrc.test.ts | 56 ++++++++++++++++++++++++++++++++++++++++++
2 files changed, 89 insertions(+), 23 deletions(-)
gate history · 2 passed · 2 rejected · iteration 6
evidence per changed file
file reads edits tests
src/install/PackageManager.rs 4 3 0
test/cli/install/npmrc.test.ts 2 5 0