From ba692da36c130a73dc83cebdaa3a85254d55efa4 Mon Sep 17 00:00:00 2001 From: grishencorp Date: Tue, 22 Sep 2026 18:39:35 -0300 Subject: [PATCH] Fix duplicate npm configuration paths in direct publisher --- scripts/publish-direct-npm-release.mjs | 5 +++++ tests/direct-npm-release.test.mjs | 23 +++++++++++++++++++++-- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/scripts/publish-direct-npm-release.mjs b/scripts/publish-direct-npm-release.mjs index 2a1b4a3d..cbae85bf 100644 --- a/scripts/publish-direct-npm-release.mjs +++ b/scripts/publish-direct-npm-release.mjs @@ -194,6 +194,11 @@ export async function publishDirectNpmRelease({ }; const report = path.join(root, "direct-release-report.json"); try { + // npm rejects loading one path as both user and global configuration. + clean.NPM_CONFIG_USERCONFIG = path.join(cwd, "user.npmrc"); + clean.NPM_CONFIG_GLOBALCONFIG = path.join(cwd, "global.npmrc"); + await writeFile(clean.NPM_CONFIG_USERCONFIG, "", { mode: 0o600 }); + await writeFile(clean.NPM_CONFIG_GLOBALCONFIG, "", { mode: 0o600 }); const version = execute("npm", ["--version"], { cwd, environment: clean, diff --git a/tests/direct-npm-release.test.mjs b/tests/direct-npm-release.test.mjs index a63aca5e..ec79cb6b 100644 --- a/tests/direct-npm-release.test.mjs +++ b/tests/direct-npm-release.test.mjs @@ -1,3 +1,4 @@ +import { spawnSync } from "node:child_process"; import assert from "node:assert/strict"; import test from "node:test"; import { createHash, generateKeyPairSync, sign } from "node:crypto"; @@ -191,8 +192,8 @@ test("publishes only exact tarballs, dependencies first, from a credential-free assert(c.args.includes("--ignore-scripts")); assert(c.args.includes("--provenance")); assert(c.args.includes("--registry=https://registry.npmjs.org/")); - assert.equal(c.opts.environment.NPM_CONFIG_USERCONFIG, "/dev/null"); - assert.equal(c.opts.environment.NPM_CONFIG_GLOBALCONFIG, "/dev/null"); + assert.equal(c.opts.environment.NPM_CONFIG_USERCONFIG, path.join(c.opts.cwd, "user.npmrc")); + assert.equal(c.opts.environment.NPM_CONFIG_GLOBALCONFIG, path.join(c.opts.cwd, "global.npmrc")); assert.notEqual(c.opts.cwd, f.root); assert.notEqual(c.opts.cwd, f.dir); } @@ -447,3 +448,21 @@ test("the workflow keeps preparation unprivileged, publication gated and registr assert.match(verify, /AGENTPLAT_REGISTRY_CONSUMER_PROFILE: postgres/); assert.match(verify, /node-version: 22\.22\.0/); }); + + test("real npm loads the isolated publication configuration", async (t) => { + const f = await fixture(t); + await publishDirectNpmRelease({ + ...f, + environment: environment(), + fetchImplementation: freshRegistry, + execute(command, args, options) { + const result = spawnSync(command, ["--version"], { + cwd: options.cwd, + env: { ...process.env, ...options.environment }, + encoding: "utf8", + }); + assert.equal(result.status, 0, result.stderr); + return result; + }, + }); +});