fix: resolve npm for portable runtime packaging

This commit is contained in:
mesalogo
2026-09-09 01:43:53 +08:00
parent b1ba14f98e
commit cbb6398851
4 changed files with 97 additions and 30 deletions
+33
View File
@@ -0,0 +1,33 @@
const { existsSync } = require("node:fs");
const { join } = require("node:path");
function npmInvocation(projectDir, environment = process.env) {
const npmCli = environment.npm_execpath;
if (npmCli) {
return {
command: process.execPath,
prefixArgs: [npmCli],
};
}
const bundledNpmCli = join(
projectDir,
"node_modules",
"npm",
"bin",
"npm-cli.js",
);
if (existsSync(bundledNpmCli)) {
return {
command: process.execPath,
prefixArgs: [bundledNpmCli],
};
}
return {
command: process.platform === "win32" ? "npm.cmd" : "npm",
prefixArgs: [],
};
}
module.exports = { npmInvocation };
+2 -12
View File
@@ -8,21 +8,11 @@ const {
} = require("node:fs");
const { join } = require("node:path");
const { spawnSync } = require("node:child_process");
const { npmInvocation } = require("./npm-invocation.cjs");
const opencodeVersion = "1.18.29";
const opencodePluginPackage = "@opencode-ai/plugin";
function npmInvocation() {
const npmCli = process.env.npm_execpath;
if (npmCli) {
return { command: process.execPath, prefixArgs: [npmCli] };
}
if (process.platform === "win32") {
throw new Error("npm_execpath is required to prepare OpenCode config");
}
return { command: "npm", prefixArgs: [] };
}
function prepareBundledOpenCodeConfig(projectDir) {
const targetDirectory = join(
projectDir,
@@ -68,7 +58,7 @@ function prepareBundledOpenCodeConfig(projectDir) {
"utf8",
);
writeFileSync(join(stagingDirectory, ".gitignore"), "node_modules\n");
const npm = npmInvocation();
const npm = npmInvocation(projectDir);
const environment = { ...process.env };
delete environment.NODE_TLS_REJECT_UNAUTHORIZED;
const result = spawnSync(
+2 -18
View File
@@ -17,6 +17,7 @@ const {
opencodeVersion,
prepareBundledOpenCodeConfig,
} = require("./opencode-config.cjs");
const { npmInvocation } = require("./npm-invocation.cjs");
const architectureNames = {
1: "x64",
@@ -48,23 +49,6 @@ async function lockedIntegrity(projectDir, packageName) {
return entry.integrity;
}
function npmInvocation() {
const npmCli = process.env.npm_execpath;
if (npmCli) {
return {
command: process.execPath,
prefixArgs: [npmCli],
};
}
if (process.platform === "win32") {
throw new Error("npm_execpath is required to prepare bundled runtimes");
}
return {
command: "npm",
prefixArgs: [],
};
}
async function downloadPackage(projectDir, packageName, integrity) {
const cacheDirectory = join(projectDir, ".runtime-resources", "cache");
await mkdir(cacheDirectory, { recursive: true });
@@ -80,7 +64,7 @@ async function downloadPackage(projectDir, packageName, integrity) {
await rm(archivePath, { force: true });
}
const npm = npmInvocation();
const npm = npmInvocation(projectDir);
const result = spawnSync(
npm.command,
[
+60
View File
@@ -0,0 +1,60 @@
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'
import { createRequire } from 'node:module'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { afterEach, beforeEach, describe, expect, it } from 'vitest'
interface NpmInvocationModule {
npmInvocation(
projectDir: string,
environment?: NodeJS.ProcessEnv
): {
command: string
prefixArgs: string[]
}
}
const require = createRequire(import.meta.url)
const { npmInvocation } = require(
'../build/npm-invocation.cjs'
) as NpmInvocationModule
let directory: string
beforeEach(() => {
directory = mkdtempSync(join(tmpdir(), 'goodbuddy-npm-invocation-'))
})
afterEach(() => {
rmSync(directory, { recursive: true, force: true })
})
describe('npm invocation', () => {
it('uses npm_execpath when the package manager provides it', () => {
expect(
npmInvocation(directory, { npm_execpath: 'provided-npm-cli.js' })
).toEqual({
command: process.execPath,
prefixArgs: ['provided-npm-cli.js']
})
})
it('uses the project npm CLI when npm_execpath is absent', () => {
const npmCli = join(
directory,
'node_modules',
'npm',
'bin',
'npm-cli.js'
)
mkdirSync(join(directory, 'node_modules', 'npm', 'bin'), {
recursive: true
})
writeFileSync(npmCli, '')
expect(npmInvocation(directory, {})).toEqual({
command: process.execPath,
prefixArgs: [npmCli]
})
})
})