165 lines · 6.5 KB
/**
 * Shared harness for the merge-fix e2e and eval scripts: builds a repo with a conflicting pull request over
 * git as the GITFOR_TOKEN user, and runs a fix. Tokens live in the account's Durable Object, so create one in
 * the dev server's settings first. D1_CWD points at the checkout whose dev server is under test (its
 * `.wrangler` state holds the D1 directory).
 */
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { dirname, join } from "node:path";

export const BASE_URL = (process.env.BASE_URL ?? "http://localhost:3015").replace(/\/$/, "");
const POLL_MS = 3000;
const D1_CWD = process.env.D1_CWD ?? process.cwd();
export const FIX_TIMEOUT_MS = 300_000;
export const CI_TIMEOUT_MS = 60_000;
/** A merge commit lists itself plus two parents in `rev-list --parents`. */
export const MERGE_PARENT_FIELDS = 3;
/** Two generations and one Clef decision for the single hunk. */
export const EXPECTED_MIN_CALLS = 3;

/**
 * Git runs with no system or global config, so credential helpers (osxkeychain) never see the token in
 * the push URL and never prompt.
 */
const GIT_ENV = { ...process.env, GIT_CONFIG_NOSYSTEM: "1", GIT_CONFIG_GLOBAL: "/dev/null", GIT_TERMINAL_PROMPT: "0" };

export function sh(cmd: string[], cwd?: string): string {
  const result = Bun.spawnSync(cmd, { cwd, stdout: "pipe", stderr: "pipe", env: GIT_ENV });
  if (result.exitCode !== 0) throw new Error(`${cmd.join(" ")} failed: ${result.stderr.toString()}`);
  return result.stdout.toString();
}

export function d1(sql: string): Array<Record<string, unknown>> {
  const out = sh(["bunx", "wrangler", "d1", "execute", "gitfor", "--local", "--json", "--command", sql], D1_CWD);
  return (JSON.parse(out) as Array<{ results: Array<Record<string, unknown>> }>)[0]?.results ?? [];
}

function requiredToken(): string {
  const token = process.env.GITFOR_TOKEN;
  if (!token) throw new Error("Set GITFOR_TOKEN to a classic token (repo scope) of a user on the dev server");