fix: resolve npm for portable runtime packaging
This commit is contained in:
@@ -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 };
|
||||||
@@ -8,21 +8,11 @@ const {
|
|||||||
} = require("node:fs");
|
} = require("node:fs");
|
||||||
const { join } = require("node:path");
|
const { join } = require("node:path");
|
||||||
const { spawnSync } = require("node:child_process");
|
const { spawnSync } = require("node:child_process");
|
||||||
|
const { npmInvocation } = require("./npm-invocation.cjs");
|
||||||
|
|
||||||
const opencodeVersion = "1.18.29";
|
const opencodeVersion = "1.18.29";
|
||||||
const opencodePluginPackage = "@opencode-ai/plugin";
|
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) {
|
function prepareBundledOpenCodeConfig(projectDir) {
|
||||||
const targetDirectory = join(
|
const targetDirectory = join(
|
||||||
projectDir,
|
projectDir,
|
||||||
@@ -68,7 +58,7 @@ function prepareBundledOpenCodeConfig(projectDir) {
|
|||||||
"utf8",
|
"utf8",
|
||||||
);
|
);
|
||||||
writeFileSync(join(stagingDirectory, ".gitignore"), "node_modules\n");
|
writeFileSync(join(stagingDirectory, ".gitignore"), "node_modules\n");
|
||||||
const npm = npmInvocation();
|
const npm = npmInvocation(projectDir);
|
||||||
const environment = { ...process.env };
|
const environment = { ...process.env };
|
||||||
delete environment.NODE_TLS_REJECT_UNAUTHORIZED;
|
delete environment.NODE_TLS_REJECT_UNAUTHORIZED;
|
||||||
const result = spawnSync(
|
const result = spawnSync(
|
||||||
|
|||||||
+2
-18
@@ -17,6 +17,7 @@ const {
|
|||||||
opencodeVersion,
|
opencodeVersion,
|
||||||
prepareBundledOpenCodeConfig,
|
prepareBundledOpenCodeConfig,
|
||||||
} = require("./opencode-config.cjs");
|
} = require("./opencode-config.cjs");
|
||||||
|
const { npmInvocation } = require("./npm-invocation.cjs");
|
||||||
|
|
||||||
const architectureNames = {
|
const architectureNames = {
|
||||||
1: "x64",
|
1: "x64",
|
||||||
@@ -48,23 +49,6 @@ async function lockedIntegrity(projectDir, packageName) {
|
|||||||
return entry.integrity;
|
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) {
|
async function downloadPackage(projectDir, packageName, integrity) {
|
||||||
const cacheDirectory = join(projectDir, ".runtime-resources", "cache");
|
const cacheDirectory = join(projectDir, ".runtime-resources", "cache");
|
||||||
await mkdir(cacheDirectory, { recursive: true });
|
await mkdir(cacheDirectory, { recursive: true });
|
||||||
@@ -80,7 +64,7 @@ async function downloadPackage(projectDir, packageName, integrity) {
|
|||||||
await rm(archivePath, { force: true });
|
await rm(archivePath, { force: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
const npm = npmInvocation();
|
const npm = npmInvocation(projectDir);
|
||||||
const result = spawnSync(
|
const result = spawnSync(
|
||||||
npm.command,
|
npm.command,
|
||||||
[
|
[
|
||||||
|
|||||||
@@ -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]
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user