Files
MineGeo/lib/bug-lifecycle.ts

99 lines
2.7 KiB
TypeScript

import "server-only";
import { randomUUID } from "node:crypto";
import { start } from "workflow/api";
import { admin, required } from "./supabase";
import { LinearSupportClient } from "./linear-support-client";
import { cleanupDoneBugReport } from "./bug-cleanup-workflow";
export async function scheduleBugCleanup(id: string) {
if (!process.env.LINEAR_API_KEY || !process.env.LINEAR_TEAM_ID) return;
const db = admin(),
claim = `starting:${randomUUID()}`;
const rows = required(
await db.rpc("claim_bug_cleanup", { p_report: id, p_worker: claim }),
);
for (const report of rows) {
try {
const run = await start(cleanupDoneBugReport, [
id,
report.closed_at!,
claim,
]);
required(
await db
.from("bug_reports")
.update({ cleanup_run_id: run.runId, cleanup_lease_until: null })
.eq("id", id)
.eq("cleanup_run_id", claim)
.select("id"),
);
} catch {
console.error("Bug report cleanup remains saved for retry.");
}
}
}
export async function reconcileBugStatus(id: string, force = false) {
const db = admin();
const lookup = await db
.from("bug_reports")
.select("*")
.eq("id", id)
.maybeSingle();
if (lookup.error) required(lookup);
const report = lookup.data;
if (!report?.issue_synced || report.purge_started_at) return;
if (report.state === "done" && !report.linear_done_synced) {
await scheduleBugCleanup(id);
return;
}
if (
!force &&
report.status_checked_at &&
Date.parse(report.status_checked_at) > Date.now() - 60000
)
return;
required(
await db
.from("bug_reports")
.update({ status_checked_at: new Date().toISOString() })
.eq("id", id)
.select("id"),
);
const linear = new LinearSupportClient(
process.env.LINEAR_API_KEY!,
process.env.LINEAR_TEAM_ID!,
process.env.LINEAR_PROJECT_ID,
);
const issue = await linear.issue(id);
if (!issue) {
if (report.state === "done") await scheduleBugCleanup(id);
return;
}
if (issue.state?.type === "completed") {
required(
await db.rpc("set_bug_report_done", {
p_report: id,
p_done: true,
p_closed_at: issue.completedAt ?? null,
}),
);
// An external completed status may need normalizing to the team's Done status.
required(
await db
.from("bug_reports")
.update({
linear_done_synced: issue.state.name.toLowerCase() === "done",
})
.eq("id", id)
.eq("state", "done")
.select("id"),
);
await scheduleBugCleanup(id);
} else if (report.state === "done") {
required(
await db.rpc("set_bug_report_done", { p_report: id, p_done: false }),
);
}
}