From 92c05428ecb8eda908a16b04a053811a425f6846 Mon Sep 17 00:00:00 2001 From: suzmii Date: Mon, 21 Sep 2026 12:06:39 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8F=A6=E4=B8=80=E6=9E=B6=E6=9E=84=E5=88=87?= =?UTF-8?q?=E7=89=87=E4=B8=8D=E5=BE=97=E5=80=9F=E7=94=A8=E5=AE=BF=E4=B8=BB?= =?UTF-8?q?=20npm=20-=20runtimeSourceForTarget=20=E5=AF=B9=E9=9D=9E?= =?UTF-8?q?=E5=AE=BF=E4=B8=BB=E6=9E=B6=E6=9E=84=E8=BF=94=E5=9B=9E=20npmCli?= =?UTF-8?q?:=20null=EF=BC=8C=E6=98=8E=E4=BB=A4=E6=8C=89=E8=AF=A5=E5=8F=91?= =?UTF-8?q?=E8=A1=8C=E7=89=88=E8=87=AA=E5=B7=B1=E7=9A=84=E5=AE=89=E8=A3=85?= =?UTF-8?q?=E7=9B=AE=E5=BD=95=E6=9F=A5=E6=89=BE=20npm=20-=20=E8=A6=86?= =?UTF-8?q?=E7=9B=96=E7=BB=8F=20npm=20run=20=E8=A7=A6=E5=8F=91=EF=BC=88npm?= =?UTF-8?q?=5Fexecpath=20=E6=8C=87=E5=90=91=E5=AE=BF=E4=B8=BB=20npm?= =?UTF-8?q?=EF=BC=89=E6=97=B6=E4=B8=A4=E4=BB=BD=E5=88=87=E7=89=87=E4=BB=8D?= =?UTF-8?q?=E5=90=84=E5=B8=A6=E8=87=AA=E5=B7=B1=E9=82=A3=E4=BB=BD=20npm=20?= =?UTF-8?q?=E7=9A=84=E7=94=A8=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../scripts/stage-node-runtime.mjs | 5 +++- .../scripts/stage-node-runtime.test.mjs | 26 ++++++++++++++++--- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/apps/ai-game-creator-shell/scripts/stage-node-runtime.mjs b/apps/ai-game-creator-shell/scripts/stage-node-runtime.mjs index 9b6c5a934..e2fe1b8d1 100644 --- a/apps/ai-game-creator-shell/scripts/stage-node-runtime.mjs +++ b/apps/ai-game-creator-shell/scripts/stage-node-runtime.mjs @@ -132,7 +132,9 @@ export function runtimeSourceForTarget( } return { nodePath: configured, - npmCli: undefined, + // 明令不使用宿主 npm(`npm_execpath`):另一架构的 npm 必须来自它自己那份发行版 + // 安装目录,否则两份切片的 npm 来源不同源,架构与来源对不上。null 表示"按 node 目录查找"。 + npmCli: null, licensePath: env[`AGC_NODE_RUNTIME_${key}_LICENSE_PATH`]?.trim(), }; } @@ -372,6 +374,7 @@ export function inspectRuntimeSource( ); } const nodeDirectory = path.dirname(node); + // `npmCli: null` 表示显式拒绝沿用宿主 npm,只按这份 node 自己的安装目录查找。 const npmCandidates = [ npmCli, path.join(nodeDirectory, 'node_modules/npm/bin/npm-cli.js'), diff --git a/apps/ai-game-creator-shell/scripts/stage-node-runtime.test.mjs b/apps/ai-game-creator-shell/scripts/stage-node-runtime.test.mjs index 23836fe21..c42ee8754 100644 --- a/apps/ai-game-creator-shell/scripts/stage-node-runtime.test.mjs +++ b/apps/ai-game-creator-shell/scripts/stage-node-runtime.test.mjs @@ -376,10 +376,20 @@ test('universal stages one runtime per architecture with architecture-correct ma 'aarch64-apple-darwin', 'x86_64-apple-darwin', ]); - const manifests = stageNodeRuntimeForTarget( - 'universal-apple-darwin', - optionsFor(root, sources), - ); + // 模拟经 `npm run` 触发的构建:npm 把宿主的 npm-cli.js 放进环境变量。 + // 另一架构的切片必须无视它,只能使用自己发行版目录里的 npm。 + const previousNpmExecPath = process.env.npm_execpath; + process.env.npm_execpath = sources.arm64.npmCli; + let manifests; + try { + manifests = stageNodeRuntimeForTarget( + 'universal-apple-darwin', + optionsFor(root, sources), + ); + } finally { + if (previousNpmExecPath === undefined) delete process.env.npm_execpath; + else process.env.npm_execpath = previousNpmExecPath; + } assert.deepEqual( manifests.map((manifest) => `${manifest.platform}-${manifest.arch}`), ['darwin-arm64', 'darwin-x64'], @@ -400,6 +410,14 @@ test('universal stages one runtime per architecture with architecture-correct ma assert.ok( fs.existsSync(path.join(directory, 'node_modules/npm/LICENSE')), ); + // 每份切片必须自带对应架构发行版的 npm,不能借用宿主那一份。 + assert.equal( + fs.readFileSync( + path.join(directory, 'node_modules/npm/bin/npm-cli.js'), + 'utf8', + ), + `// ${manifest.arch}`, + ); } // 单架构目标仍写扁平目录(与既有发布一致),不产生分架构子目录。 const flat = stageNodeRuntimeForTarget(