import { spawn, spawnSync } from "node:child_process"; import { createWriteStream, existsSync, readFileSync } from "node:fs"; export interface CommandResult { command: string[]; cwd: string; exitCode: number | null; stdout: string; stderr: string; } export function runCommand(command: string[], cwd: string): CommandResult { const result = spawnSync(command[0], command.slice(1), { cwd, encoding: "utf8", maxBuffer: 1024 * 1024 * 8, }); return { command, cwd, exitCode: result.status, stdout: result.stdout ?? "", stderr: result.stderr ?? result.error?.message ?? "", }; } export function commandOk(command: string[], cwd: string): boolean { return runCommand(command, cwd).exitCode === 0; } export async function runCommandToFiles(command: string[], cwd: string, stdoutFile: string, stderrFile: string): Promise { const stdout = createWriteStream(stdoutFile, { flags: "a" }); const stderr = createWriteStream(stderrFile, { flags: "a" }); stdout.write(`$ ${command.map((part) => JSON.stringify(part)).join(" ")}\n`); const child = spawn(command[0], command.slice(1), { cwd, env: process.env }); child.stdout.pipe(stdout, { end: false }); child.stderr.pipe(stderr, { end: false }); const exitCode = await new Promise((resolve) => { child.on("close", (code) => resolve(code)); child.on("error", (error) => { stderr.write(`${error.stack ?? error.message}\n`); resolve(127); }); }); stdout.write(`\n[exit_code=${exitCode}]\n`); stdout.end(); stderr.end(); return exitCode; } export function tailFile(path: string, maxBytes = 8192): string { if (!existsSync(path)) return ""; const content = readFileSync(path); return content.subarray(Math.max(0, content.length - maxBytes)).toString("utf8"); }