import { spawn } from "node:child_process";
export class ChildProcessRunner {
    cwd;
    artifacts;
    constructor(cwd, artifacts) {
        this.cwd = cwd;
        this.artifacts = artifacts;
    }
    async run(options) {
        const started = Date.now();
        const commandLine = [options.command, ...options.args].join(" ");
        const child = spawn(options.command, options.args, {
            cwd: this.cwd,
            env: mergeEnv(process.env, options.env ?? {}),
            stdio: ["ignore", "pipe", "pipe"],
        });
        let stdout = "";
        let stderr = "";
        let timedOut = false;
        const timer = options.timeoutMs === undefined
            ? undefined
            : setTimeout(() => {
                timedOut = true;
                child.kill("SIGTERM");
            }, options.timeoutMs);
        child.stdout?.on("data", (chunk) => {
            stdout += chunk.toString("utf8");
        });
        child.stderr?.on("data", (chunk) => {
            stderr += chunk.toString("utf8");
        });
        const exit = await new Promise((resolve, reject) => {
            child.on("error", reject);
            child.on("exit", (exitCode, signal) => resolve({ exitCode, signal }));
        });
        if (timer !== undefined) {
            clearTimeout(timer);
        }
        const result = {
            command: options.command,
            args: options.args,
            exitCode: exit.exitCode,
            signal: exit.signal,
            timedOut,
            durationMs: Date.now() - started,
            stdout,
            stderr,
            logPath: "",
        };
        result.logPath = this.artifacts.writeText(`logs/${options.logName}.log`, [
            `$ ${commandLine}`,
            "",
            "## stdout",
            stdout,
            "",
            "## stderr",
            stderr,
            "",
        ].join("\n"));
        return result;
    }
}
function mergeEnv(base, overrides) {
    const env = { ...base };
    for (const [key, value] of Object.entries(overrides)) {
        if (value === undefined) {
            delete env[key];
        }
        else {
            env[key] = value;
        }
    }
    return env;
}
