mirror of
https://github.com/moltbot/moltbot.git
synced 2026-04-23 22:55:24 +00:00
fix: route pnpm test wrappers through the active runner (#60153)
This commit is contained in:
53
scripts/pnpm-runner.mjs
Normal file
53
scripts/pnpm-runner.mjs
Normal file
@@ -0,0 +1,53 @@
|
||||
import path from "node:path";
|
||||
|
||||
const WINDOWS_UNSAFE_CMD_CHARS_RE = /[&|<>%\r\n]/;
|
||||
|
||||
function isPnpmExecPath(value) {
|
||||
return /^pnpm(?:-cli)?(?:\.(?:c?js|cmd|exe))?$/.test(path.basename(value).toLowerCase());
|
||||
}
|
||||
|
||||
function escapeForCmdExe(arg) {
|
||||
if (WINDOWS_UNSAFE_CMD_CHARS_RE.test(arg)) {
|
||||
throw new Error(`unsafe Windows cmd.exe argument detected: ${JSON.stringify(arg)}`);
|
||||
}
|
||||
const escaped = arg.replace(/\^/g, "^^");
|
||||
if (!escaped.includes(" ") && !escaped.includes('"')) {
|
||||
return escaped;
|
||||
}
|
||||
return `"${escaped.replace(/"/g, '""')}"`;
|
||||
}
|
||||
|
||||
function buildCmdExeCommandLine(command, args) {
|
||||
return [escapeForCmdExe(command), ...args.map(escapeForCmdExe)].join(" ");
|
||||
}
|
||||
|
||||
export function resolvePnpmRunner(params = {}) {
|
||||
const pnpmArgs = params.pnpmArgs ?? [];
|
||||
const npmExecPath = params.npmExecPath ?? process.env.npm_execpath;
|
||||
const nodeExecPath = params.nodeExecPath ?? process.execPath;
|
||||
const platform = params.platform ?? process.platform;
|
||||
const comSpec = params.comSpec ?? process.env.ComSpec ?? "cmd.exe";
|
||||
|
||||
if (typeof npmExecPath === "string" && npmExecPath.length > 0 && isPnpmExecPath(npmExecPath)) {
|
||||
return {
|
||||
command: nodeExecPath,
|
||||
args: [npmExecPath, ...pnpmArgs],
|
||||
shell: false,
|
||||
};
|
||||
}
|
||||
|
||||
if (platform === "win32") {
|
||||
return {
|
||||
command: comSpec,
|
||||
args: ["/d", "/s", "/c", buildCmdExeCommandLine("pnpm.cmd", pnpmArgs)],
|
||||
shell: false,
|
||||
windowsVerbatimArguments: true,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
command: "pnpm",
|
||||
args: pnpmArgs,
|
||||
shell: false,
|
||||
};
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import { spawn } from "node:child_process";
|
||||
import { resolvePnpmRunner } from "./pnpm-runner.mjs";
|
||||
|
||||
const forwardedArgs = [];
|
||||
let quietOverride;
|
||||
@@ -24,15 +25,15 @@ const env = {
|
||||
OPENCLAW_LIVE_TEST_QUIET: quietOverride ?? process.env.OPENCLAW_LIVE_TEST_QUIET ?? "1",
|
||||
};
|
||||
|
||||
const command = process.platform === "win32" ? "pnpm.cmd" : "pnpm";
|
||||
const child = spawn(
|
||||
command,
|
||||
["exec", "vitest", "run", "--config", "vitest.live.config.ts", ...forwardedArgs],
|
||||
{
|
||||
stdio: "inherit",
|
||||
env,
|
||||
},
|
||||
);
|
||||
const pnpmRunner = resolvePnpmRunner({
|
||||
pnpmArgs: ["exec", "vitest", "run", "--config", "vitest.live.config.ts", ...forwardedArgs],
|
||||
});
|
||||
const child = spawn(pnpmRunner.command, pnpmRunner.args, {
|
||||
stdio: "inherit",
|
||||
env: pnpmRunner.env ?? env,
|
||||
shell: pnpmRunner.shell,
|
||||
windowsVerbatimArguments: pnpmRunner.windowsVerbatimArguments,
|
||||
});
|
||||
|
||||
child.on("exit", (code, signal) => {
|
||||
if (signal) {
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { spawn } from "node:child_process";
|
||||
import { acquireLocalHeavyCheckLockSync } from "./lib/local-heavy-check-runtime.mjs";
|
||||
import { resolvePnpmRunner } from "./pnpm-runner.mjs";
|
||||
import { buildVitestArgs } from "./test-projects.test-support.mjs";
|
||||
|
||||
const command = process.platform === "win32" ? "pnpm.cmd" : "pnpm";
|
||||
const vitestArgs = buildVitestArgs(process.argv.slice(2));
|
||||
const pnpmRunner = resolvePnpmRunner({ pnpmArgs: vitestArgs });
|
||||
const releaseLock = acquireLocalHeavyCheckLockSync({
|
||||
cwd: process.cwd(),
|
||||
env: process.env,
|
||||
@@ -20,10 +21,11 @@ const releaseLockOnce = () => {
|
||||
releaseLock();
|
||||
};
|
||||
|
||||
const child = spawn(command, vitestArgs, {
|
||||
const child = spawn(pnpmRunner.command, pnpmRunner.args, {
|
||||
stdio: "inherit",
|
||||
env: process.env,
|
||||
shell: process.platform === "win32",
|
||||
env: pnpmRunner.env ?? process.env,
|
||||
shell: pnpmRunner.shell,
|
||||
windowsVerbatimArguments: pnpmRunner.windowsVerbatimArguments,
|
||||
});
|
||||
|
||||
child.on("exit", (code, signal) => {
|
||||
|
||||
70
test/scripts/pnpm-runner.test.ts
Normal file
70
test/scripts/pnpm-runner.test.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { resolvePnpmRunner } from "../../scripts/pnpm-runner.mjs";
|
||||
|
||||
describe("resolvePnpmRunner", () => {
|
||||
it("uses npm_execpath when it points to pnpm", () => {
|
||||
expect(
|
||||
resolvePnpmRunner({
|
||||
npmExecPath: "/home/test/.cache/node/corepack/v1/pnpm/10.32.1/bin/pnpm.cjs",
|
||||
nodeExecPath: "/usr/local/bin/node",
|
||||
pnpmArgs: ["exec", "vitest", "run"],
|
||||
platform: "linux",
|
||||
}),
|
||||
).toEqual({
|
||||
command: "/usr/local/bin/node",
|
||||
args: [
|
||||
"/home/test/.cache/node/corepack/v1/pnpm/10.32.1/bin/pnpm.cjs",
|
||||
"exec",
|
||||
"vitest",
|
||||
"run",
|
||||
],
|
||||
shell: false,
|
||||
});
|
||||
});
|
||||
|
||||
it("falls back to bare pnpm on non-Windows when npm_execpath is missing", () => {
|
||||
expect(
|
||||
resolvePnpmRunner({
|
||||
npmExecPath: "",
|
||||
pnpmArgs: ["exec", "vitest", "run"],
|
||||
platform: "linux",
|
||||
}),
|
||||
).toEqual({
|
||||
command: "pnpm",
|
||||
args: ["exec", "vitest", "run"],
|
||||
shell: false,
|
||||
});
|
||||
});
|
||||
|
||||
it("wraps pnpm.cmd via cmd.exe on Windows when npm_execpath is unavailable", () => {
|
||||
expect(
|
||||
resolvePnpmRunner({
|
||||
comSpec: "C:\\Windows\\System32\\cmd.exe",
|
||||
npmExecPath: "",
|
||||
pnpmArgs: ["exec", "vitest", "run", "-t", "path with spaces"],
|
||||
platform: "win32",
|
||||
}),
|
||||
).toEqual({
|
||||
command: "C:\\Windows\\System32\\cmd.exe",
|
||||
args: ["/d", "/s", "/c", 'pnpm.cmd exec vitest run -t "path with spaces"'],
|
||||
shell: false,
|
||||
windowsVerbatimArguments: true,
|
||||
});
|
||||
});
|
||||
|
||||
it("escapes caret arguments for Windows cmd.exe", () => {
|
||||
expect(
|
||||
resolvePnpmRunner({
|
||||
comSpec: "C:\\Windows\\System32\\cmd.exe",
|
||||
npmExecPath: "",
|
||||
pnpmArgs: ["exec", "vitest", "-t", "@scope/pkg@^1.2.3"],
|
||||
platform: "win32",
|
||||
}),
|
||||
).toEqual({
|
||||
command: "C:\\Windows\\System32\\cmd.exe",
|
||||
args: ["/d", "/s", "/c", "pnpm.cmd exec vitest -t @scope/pkg@^^1.2.3"],
|
||||
shell: false,
|
||||
windowsVerbatimArguments: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user