117 lines
2.8 KiB
TypeScript

import { execFileSync } from "node:child_process";
import { resolve } from "node:path";
import type { LibSQLDatabase } from "drizzle-orm/libsql";
import { snapshots } from "./schema.ts";
const GIT_TIMEOUT_MS = 15_000;
function workspaceRoot(): string {
const raw = process.env.GIT_WORKSPACE_ROOT;
return raw ? resolve(raw) : resolve(process.cwd());
}
function gitErrorMessage(err: unknown): string {
if (err instanceof Error) {
const m = err.message.trim();
return m.length > 200 ? `${m.slice(0, 197)}...` : m;
}
return String(err);
}
function runGit(cwd: string, args: string[]): string {
return execFileSync("git", args, {
cwd,
encoding: "utf8",
timeout: GIT_TIMEOUT_MS,
maxBuffer: 2 * 1024 * 1024,
}).trimEnd();
}
function countPorcelainLines(output: string): number {
if (!output) return 0;
return output.split("\n").filter((line) => line.length > 0).length;
}
export async function compute(db: LibSQLDatabase, _peers: unknown) {
const root = workspaceRoot();
const ts = Date.now();
let branch = "";
let headShort = "";
let porcelainLines = 0;
let hasUpstream = 0;
let aheadCount = 0;
let behindCount = 0;
let gitError = "";
try {
const inside = runGit(root, ["rev-parse", "--is-inside-work-tree"]).trim();
if (inside !== "true") {
gitError = "not a git work tree";
await db.insert(snapshots).values({
ts,
branch,
headShort,
porcelainLines,
hasUpstream,
aheadCount,
behindCount,
gitError,
});
return {
workspaceRoot: root,
branch,
headShort,
porcelainLines,
hasUpstream: false,
aheadCount,
behindCount,
gitError,
};
}
branch = runGit(root, ["rev-parse", "--abbrev-ref", "HEAD"]);
headShort = runGit(root, ["rev-parse", "--short", "HEAD"]);
porcelainLines = countPorcelainLines(runGit(root, ["status", "--porcelain"]));
try {
runGit(root, ["rev-parse", "--abbrev-ref", "@{upstream}"]);
hasUpstream = 1;
const lb = runGit(root, ["rev-list", "--left-right", "--count", "HEAD...@{upstream}"]);
const parts = lb.split(/[\t\s]+/).filter(Boolean);
if (parts.length >= 2) {
aheadCount = Number.parseInt(parts[0], 10) || 0;
behindCount = Number.parseInt(parts[1], 10) || 0;
}
} catch {
hasUpstream = 0;
aheadCount = 0;
behindCount = 0;
}
} catch (e) {
gitError = gitErrorMessage(e);
}
await db.insert(snapshots).values({
ts,
branch,
headShort,
porcelainLines,
hasUpstream,
aheadCount,
behindCount,
gitError,
});
return {
workspaceRoot: root,
branch,
headShort,
porcelainLines,
hasUpstream: hasUpstream === 1,
aheadCount,
behindCount,
gitError: gitError || undefined,
};
}