Merge workflow and sense devDependencies into root, remove per-package package.json and workflow tsconfigs, add scripts/build.mjs for consistent outputs. Fixes #22
86 lines
2.8 KiB
JavaScript
86 lines
2.8 KiB
JavaScript
// senses/git-workspace-status/src/index.ts
|
|
import { execFileSync } from "node:child_process";
|
|
import { resolve } from "node:path";
|
|
|
|
// senses/git-workspace-status/src/schema.ts
|
|
import { integer, sqliteTable, text } from "drizzle-orm/sqlite-core";
|
|
var snapshots = sqliteTable("snapshots", {
|
|
ts: integer("ts").primaryKey(),
|
|
branch: text("branch").notNull(),
|
|
headShort: text("head_short").notNull(),
|
|
porcelainLines: integer("porcelain_lines").notNull(),
|
|
hasUpstream: integer("has_upstream").notNull(),
|
|
aheadCount: integer("ahead_count").notNull(),
|
|
behindCount: integer("behind_count").notNull(),
|
|
/** Empty string when the snapshot succeeded; otherwise a short error summary. */
|
|
gitError: text("git_error").notNull()
|
|
});
|
|
|
|
// senses/git-workspace-status/src/index.ts
|
|
var GIT_TIMEOUT_MS = 15e3;
|
|
function workspaceRoot() {
|
|
const raw = process.env.GIT_WORKSPACE_ROOT;
|
|
return raw ? resolve(raw) : resolve(process.cwd());
|
|
}
|
|
function gitErrorMessage(err) {
|
|
if (err instanceof Error) {
|
|
const m = err.message.trim();
|
|
return m.length > 200 ? `${m.slice(0, 197)}...` : m;
|
|
}
|
|
return String(err);
|
|
}
|
|
function runGit(cwd, args) {
|
|
return execFileSync("git", args, {
|
|
cwd,
|
|
encoding: "utf8",
|
|
timeout: GIT_TIMEOUT_MS,
|
|
maxBuffer: 2 * 1024 * 1024
|
|
}).trimEnd();
|
|
}
|
|
function countPorcelainLines(output) {
|
|
if (!output) return 0;
|
|
return output.split("\n").filter((line) => line.length > 0).length;
|
|
}
|
|
async function compute() {
|
|
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";
|
|
return { signal: { ts, branch, headShort, porcelainLines, hasUpstream, aheadCount, behindCount, gitError }, workflow: null };
|
|
}
|
|
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);
|
|
}
|
|
return { signal: { ts, branch, headShort, porcelainLines, hasUpstream, aheadCount, behindCount, gitError }, workflow: null };
|
|
}
|
|
export {
|
|
compute,
|
|
snapshots as table
|
|
};
|