fix: keep CLI runner cross-platform

This commit is contained in:
artale 2026-06-15 03:37:51 +02:00
parent 37b964450c
commit 3ccd535ead
2 changed files with 19 additions and 9 deletions

View File

@ -0,0 +1,14 @@
import { describe, expect, it } from "vitest";
import { runCli } from "./cli-runner.js";
describe("runCli", () => {
it("runs argv without shell interpolation", async () => {
const out = await runCli(process.execPath, ["-e", "console.log(process.argv[1])", "hello&world"], 10_000);
expect(out).toBe("hello&world");
});
it("reports nonzero exits as errors", async () => {
const out = await runCli(process.execPath, ["-e", "console.log('bad'); process.exit(7)"], 10_000);
expect(out).toContain("CLI exited 7");
});
});

View File

@ -2,19 +2,15 @@ import { spawn } from "node:child_process";
const MAX_OUTPUT_BYTES = 10 * 1024 * 1024;
function cmdQuote(value: string): string {
// ponytail: minimal cmd.exe quoting for Windows .cmd shims; callers still pass argv, not shell text.
return `"${value.replace(/(["^&|<>()%!])/g, "^$1")}"`;
function isCmdShim(bin: string): boolean {
return process.platform === "win32" && /(?:\.cmd|\.bat)$/i.test(bin);
}
export function runCli(bin: string, args: string[], timeoutMs = 120_000): Promise<string> {
return new Promise((resolve) => {
const isWin = process.platform === "win32";
const command = isWin ? "cmd.exe" : bin;
const commandArgs = isWin
? ["/d", "/s", "/c", [cmdQuote(bin), ...args.map(cmdQuote)].join(" ")]
: args;
// ponytail: only .cmd/.bat need cmd.exe; real .exe paths keep true argv semantics.
const command = isCmdShim(bin) ? process.env.ComSpec ?? "cmd.exe" : bin;
const commandArgs = isCmdShim(bin) ? ["/d", "/s", "/c", bin, ...args] : args;
const child = spawn(command, commandArgs, {
shell: false,
timeout: timeoutMs,