Skip to content
File

Blob: src/worker/do/repo/debug/checks.ts

typescript112 lines
1import type { DebugCommitCheck, DebugOidCheck } from "./types";
2 
3import { objKey } from "../repoState";
4import { doPrefix, r2LooseKey } from "@/worker/keys";
5import { isValidOid } from "@/worker/common";
6import { readCommitFromStore } from "../storage";
7import { getDb, listPackCatalog } from "../db";
8import { getActivePackCatalogSnapshot } from "../catalog";
9import { parseCommitText } from "@/worker/git/core/commitParse";
10import { findCatalogPacksContainingOid, readPackedObjectFromCatalogRows } from "./packed";
11 
12export async function debugCheckCommit(
13 ctx: DurableObjectState,
14 env: Env,
15 commit: string
16): Promise<DebugCommitCheck> {
17 const q = (commit || "").toLowerCase();
18 if (!isValidOid(q)) {
19 throw new Error("Invalid commit");
20 }
21 
22 const db = getDb(ctx.storage);
23 const prefix = doPrefix(ctx.id.toString());
24 await getActivePackCatalogSnapshot(ctx);
25 const catalogRows = await listPackCatalog(db);
26 const activeCatalogRows = catalogRows.filter((row) => row.state === "active");
27 const membership: Record<string, { hasCommit: boolean; hasTree: boolean }> = {};
28 
29 for (const row of catalogRows) {
30 membership[row.packKey] = { hasCommit: false, hasTree: false };
31 }
32 
33 for (const packKey of await findCatalogPacksContainingOid(env, catalogRows, q)) {
34 membership[packKey] = membership[packKey] || { hasCommit: false, hasTree: false };
35 membership[packKey].hasCommit = true;
36 }
37 
38 let tree: string | undefined;
39 let parents: string[] = [];
40 
41 try {
42 const packedInfo = await readPackedObjectFromCatalogRows(env, activeCatalogRows, q);
43 if (packedInfo?.type === "commit") {
44 const parsed = parseCommitText(new TextDecoder().decode(packedInfo.payload));
45 tree = parsed.tree.toLowerCase();
46 parents = parsed.parents;
47 } else {
48 const legacyInfo = await readCommitFromStore(ctx, env, prefix, q);
49 if (legacyInfo) {
50 tree = legacyInfo.tree.toLowerCase();
51 parents = legacyInfo.parents;
52 }
53 }
54 } catch {}
55 
56 const hasLooseCommit = !!(await ctx.storage.get(objKey(q)));
57 let hasLooseTree = false;
58 let hasR2LooseTree = false;
59 
60 if (tree) {
61 hasLooseTree = !!(await ctx.storage.get(objKey(tree)));
62 try {
63 const head = await env.REPO_BUCKET.head(r2LooseKey(prefix, tree));
64 hasR2LooseTree = !!head;
65 } catch {}
66 
67 for (const packKey of await findCatalogPacksContainingOid(env, catalogRows, tree)) {
68 membership[packKey] = membership[packKey] || { hasCommit: false, hasTree: false };
69 membership[packKey].hasTree = true;
70 }
71 }
72 
73 return {
74 commit: { oid: q, parents, tree },
75 presence: { hasLooseCommit, hasLooseTree, hasR2LooseTree },
76 membership,
77 };
78}
79 
80export async function debugCheckOid(
81 ctx: DurableObjectState,
82 env: Env,
83 oid: string
84): Promise<DebugOidCheck> {
85 if (!isValidOid(oid)) {
86 throw new Error(`Invalid OID: ${oid}`);
87 }
88 
89 const prefix = doPrefix(ctx.id.toString());
90 const hasLoose = !!(await ctx.storage.get(objKey(oid)));
91 
92 let hasR2Loose = false;
93 try {
94 const head = await env.REPO_BUCKET.head(r2LooseKey(prefix, oid));
95 hasR2Loose = !!head;
96 } catch {}
97 
98 const db = getDb(ctx.storage);
99 await getActivePackCatalogSnapshot(ctx);
100 const catalogRows = await listPackCatalog(db);
101 const inPacks = await findCatalogPacksContainingOid(env, catalogRows, oid);
102 
103 return {
104 oid,
105 presence: {
106 hasLoose,
107 hasR2Loose,
108 },
109 inPacks,
110 };
111}