syntaxai/tdd.md · main · scripts / p620 / snapshot-git-history.ts

snapshot-git-history.ts 57 lines · 1926 bytes raw
#!/usr/bin/env bun
// Dump local git history into the same shape that the live-reports
// pipeline expects from GitHub's commits API. Runs at deploy-time so
// the container can render /reports/live for a private repo without
// holding a GitHub token. Each deploy refreshes the bundle.
//
// Output: content/git-history/<owner>__<name>.json
// Schema: GithubCommit[] (see src/c14_github.ts) — newest first.

import { spawnSync } from "node:child_process";
import { mkdirSync, writeFileSync } from "node:fs";
import { dirname, resolve } from "node:path";

const REPO_ROOT = resolve(import.meta.dir, "..", "..");
const OWNER = "syntaxai";
const NAME = "tdd.md";
const MAX = 200;

// Use ASCII record separators (\x1e between commits, \x1f between
// fields) so commit-message newlines pass through unmangled.
const FMT = ["%H", "%aI", "%an", "%ae", "%B"].join("\x1f") + "\x1e";

const res = spawnSync(
  "git",
  ["log", `--max-count=${MAX}`, `--pretty=format:${FMT}`],
  { cwd: REPO_ROOT, encoding: "utf8", maxBuffer: 64 * 1024 * 1024 },
);
if (res.status !== 0) {
  console.error("git log failed:", res.stderr);
  process.exit(1);
}

const records = res.stdout.split("\x1e").map((s) => s.trim()).filter(Boolean);
const commits = records.map((rec) => {
  const [sha, date, name, email, ...rest] = rec.split("\x1f");
  const message = (rest.join("\x1f") ?? "").replace(/\n+$/, "");
  return {
    sha: sha ?? "",
    commit: {
      author: {
        name: name ?? "",
        email: email ?? "",
        date: date ?? "",
      },
      message,
    },
    author: null,
  };
});

const outDir = resolve(REPO_ROOT, "content", "git-history");
const outPath = resolve(outDir, `${OWNER}__${NAME}.json`);
mkdirSync(dirname(outPath), { recursive: true });
writeFileSync(outPath, JSON.stringify({ owner: OWNER, name: NAME, fetchedAt: Date.now(), commits }, null, 2));

console.log(`✓ wrote ${commits.length} commits → ${outPath}`);