85 lines
2.6 KiB
TypeScript

import { writeFileSync, mkdirSync } from "node:fs";
import { join } from "node:path";
import type { Role, RoleResult } from "@uncaged/nerve-core";
import { isDryRun, spawnSafe } from "@uncaged/nerve-workflow-utils";
export type CommitterMeta = {
success: boolean;
};
export type BuildCommitterDeps = {
nerveRoot: string;
};
function logPath(nerveRoot: string): string {
return join(nerveRoot, "logs", `committer-${Date.now()}.log`);
}
function writeLog(path: string, content: string): void {
mkdirSync(join(path, ".."), { recursive: true });
writeFileSync(path, content, "utf-8");
}
export function buildCommitterRole({ nerveRoot }: BuildCommitterDeps): Role<CommitterMeta> {
return async (start, _messages) => {
const dry = isDryRun(start);
const file = logPath(nerveRoot);
if (dry) {
writeLog(file, "[dry-run] committer skipped\n");
return {
content: `[dry-run] committer skipped — log: ${file}`,
meta: { success: true },
} satisfies RoleResult<CommitterMeta>;
}
const lines: string[] = [];
let success = true;
const run = async (cmd: string, args: string[]): Promise<boolean> => {
const r = await spawnSafe(cmd, args, { cwd: nerveRoot, env: null, timeoutMs: 60_000, dryRun: false });
if (r.ok) {
lines.push(`$ ${cmd} ${args.join(" ")}`);
if (r.value.stdout) lines.push(r.value.stdout);
if (r.value.stderr) lines.push(r.value.stderr);
lines.push("");
return true;
}
const e = r.error;
lines.push(`$ ${cmd} ${args.join(" ")} — FAILED`);
if (e.kind === "non_zero_exit") {
lines.push(`exit ${e.exitCode}`);
if (e.stdout) lines.push(e.stdout);
if (e.stderr) lines.push(e.stderr);
} else if (e.kind === "timeout") {
lines.push("timeout");
if (e.stdout) lines.push(e.stdout);
if (e.stderr) lines.push(e.stderr);
} else {
lines.push(e.message);
}
lines.push("");
return false;
};
await run("git", ["add", "-A"]);
// Use a generic message; git diff will show what actually changed
const committed = await run("git", ["commit", "-m", "chore(workflow): auto-generated commit"]);
if (!committed) {
success = false;
} else {
const pushed = await run("git", ["push"]);
if (!pushed) success = false;
}
const log = lines.join("\n");
writeLog(file, log);
const summary = success ? "committed and pushed" : "commit/push failed — see log";
return {
content: `committer: ${summary}\nLog: ${file}`,
meta: { success },
} satisfies RoleResult<CommitterMeta>;
};
}