import { sql } from "drizzle-orm";
export type CheckStatus = "pass" | "fail";
export interface CheckResult {
name: string;
status: CheckStatus;
reason?: string;
}
const GREEN = "\x1b[32m";
const RED = "\x1b[31m";
const DIM = "\x1b[2m";
const RESET = "\x1b[0m";
function icon(s: CheckStatus): string {
return s === "pass" ? `${GREEN}v${RESET}` : `${RED}x${RESET}`;
}
export function checkAnthropicKey(env: NodeJS.ProcessEnv): CheckResult {
const key = env.ANTHROPIC_API_KEY;
if (!key || key.length < 10) {
return {
name: "ANTHROPIC_API_KEY set",
status: "fail",
reason:
"ANTHROPIC_API_KEY is missing — AI approval gate will block every auto-merge candidate.",
};
}
return { name: "ANTHROPIC_API_KEY set", status: "pass" };
}
export function checkAutopilotEnabled(env: NodeJS.ProcessEnv): CheckResult {
if (env.AUTOPILOT_DISABLED === "1") {
return {
name: "Autopilot enabled (AUTOPILOT_DISABLED != 1)",
status: "fail",
reason:
"AUTOPILOT_DISABLED=1 — unset (or set to 0) to let the K3 sweep run.",
};
}
return {
name: "Autopilot enabled (AUTOPILOT_DISABLED != 1)",
status: "pass",
};
}
export function checkAutoMergeSweepRegistered(
tasks: Array<{ name: string }>
): CheckResult {
const found = tasks.some((t) => t.name === "auto-merge-sweep");
if (!found) {
return {
name: "K3 auto-merge-sweep task registered",
status: "fail",
reason:
"defaultTasks() does not include 'auto-merge-sweep' — the K3 sweep is missing.",
};
}
return {
name: "K3 auto-merge-sweep task registered",
status: "pass",
};
}
export async function checkMigration0040(
runner: () => Promise<{ exists: boolean; error?: string }>
): Promise<CheckResult> {
try {
const { exists, error } = await runner();
if (error) {
return {
name: "Migration 0040 applied",
status: "fail",
reason: `column probe failed: ${error}`,
};
}
if (!exists) {
return {
name: "Migration 0040 applied",
status: "fail",
reason:
"branch_protection.enable_auto_merge column missing — run `bun run db:migrate`.",
};
}
return { name: "Migration 0040 applied", status: "pass" };
} catch (err) {
return {
name: "Migration 0040 applied",
status: "fail",
reason: err instanceof Error ? err.message : String(err),
};
}
}
async function probeAutoMergeColumn(): Promise<{ exists: boolean; error?: string }> {
try {
const { db } = await import("../src/db");
const rows = await db.execute(
sql`SELECT column_name FROM information_schema.columns
WHERE table_name = 'branch_protection'
AND column_name = 'enable_auto_merge'
LIMIT 1`
);
const list =
(rows as any).rows ?? (Array.isArray(rows) ? rows : []);
return { exists: list.length > 0 };
} catch (err) {
return {
exists: false,
error: err instanceof Error ? err.message : String(err),
};
}
}
async function getDefaultTasks(): Promise<Array<{ name: string }>> {
try {
const mod = await import("../src/lib/autopilot");
return mod.defaultTasks();
} catch {
return [];
}
}
async function main() {
console.log(
`${DIM}gluecron auto-merge readiness — ${new Date().toISOString()}${RESET}`
);
const results: CheckResult[] = [];
results.push(
await checkMigration0040(probeAutoMergeColumn)
);
results.push(checkAnthropicKey(process.env));
results.push(checkAutopilotEnabled(process.env));
results.push(checkAutoMergeSweepRegistered(await getDefaultTasks()));
for (const r of results) {
const tail = r.reason ? ` — ${r.reason}` : "";
console.log(` ${icon(r.status)} ${r.name}${tail}`);
}
const failed = results.filter((r) => r.status === "fail").length;
console.log("");
if (failed > 0) {
console.log(
`${RED}readiness FAILED — fix the items above before running enable-auto-merge.ts${RESET}`
);
process.exit(1);
}
console.log(
`${GREEN}readiness clean — safe to run enable-auto-merge.ts${RESET}`
);
process.exit(0);
}
if (import.meta.main) {
main().catch((err) => {
console.error(err instanceof Error ? err.message : String(err));
process.exit(1);
});
}
|