kanban-dev-webapp/scripts/relink.ts

82 lines
No EOL
2.3 KiB
TypeScript

/**
* One-off relink helper.
*
* Re-runs the linking engine on a project without pulling in the server-only
* modules — same pattern as scripts/backfill.ts. Run with:
* set -a && source .env.local && set +a
* npx tsx scripts/relink.ts <projectId>
*/
import PocketBase from "pocketbase";
import { resolveCommitCard, type LinkCandidate } from "../src/lib/linking/resolve";
const PB_URL = process.env.NEXT_PUBLIC_PB_URL!;
async function main() {
const projectId = process.argv[2];
if (!projectId) throw new Error("Usage: tsx scripts/relink.ts <projectId>");
const pb = new PocketBase(PB_URL);
pb.autoCancellation(false);
await pb.collection("_superusers").authWithPassword(
process.env.PB_SUPERUSER_EMAIL!,
process.env.PB_SUPERUSER_PASSWORD!
);
const cards = await pb.collection("cards").getFullList({
filter: `project = "${projectId}"`,
fields: "id,number,scope_rule,start_date,created,archived",
});
const candidates: LinkCandidate[] = cards.map((card) => ({
id: card.id,
number: card.number,
scopeRule: card.scope_rule ?? "",
startDate: card.start_date ?? "",
created: card.created,
archived: Boolean(card.archived),
}));
const commits = await pb.collection("commits").getFullList({
filter: `project = "${projectId}"`,
fields: "id,message,scope,authored_at,is_merge,card,link_source",
});
let linked = 0;
let unlinked = 0;
let preserved = 0;
for (const commit of commits) {
if (commit.link_source === "manual") {
preserved++;
continue;
}
const resolution = resolveCommitCard(
{
message: commit.message ?? "",
scope: commit.scope ?? "",
authoredAt: commit.authored_at,
isMerge: Boolean(commit.is_merge),
},
candidates
);
const nextCard = resolution.cardId ?? "";
const nextSource = resolution.source;
if (commit.card === nextCard && commit.link_source === nextSource) continue;
await pb.collection("commits").update(commit.id, {
card: nextCard,
link_source: nextSource,
});
if (nextCard) linked++;
else unlinked++;
}
console.log(
JSON.stringify({ examined: commits.length - preserved, linked, unlinked, preserved })
);
}
main().catch((e) => {
console.error(e);
process.exit(1);
});