Files
cfw-autumn/scripts/dw/helpers/emulate.ts
amianthus 25e920bbd3 feat(dw): 🎸 parallel agent worktrees with isolated infra + test harness
bun dw spins up a fully isolated per-worktree dev stack: Neon DB branch,
Dragonfly + ElasticMQ via docker compose, portless aliases, tmux
session, and emulate.dev Google OAuth. Workers, dev server, vite,
checkout, and stripe-listen run as concurrently siblings. bun setup-test
auto-runs on first dw and seeds unit-test-org via
clearOrg+setupOrg+ensureDefaultStripeAccount with 13 features + team
invites for all 5 useautumn.com members. Hardcoded localhost URLs in
test scenarios now read AUTUMN_TEST_BASE_URL/AUTUMN_TEST_VITE_URL.
preload-env.ts loads .env.local so bun t/cm/setup-test route to the
worktree server. 1265-line scripts/dw.ts split into scripts/dw/ module
(index + commands + helpers). server/src/db/initDrizzle.ts reverted to
upstream (search_path concerns solved by per-branch Neon isolation).
Workers enabled in agent worktrees now that SQS is per-worktree via
ElasticMQ.
2026-05-16 02:02:43 +01:00

66 lines
1.6 KiB
TypeScript

import { existsSync, readFileSync, rmSync } from "node:fs";
import { sh, log } from "./shell.ts";
import {
EMULATE_PID_FILE,
EMULATE_HEALTH_URL,
START_EMULATE_SH,
} from "../constants.ts";
function emulateReachable(): boolean {
const res = sh("curl", [
"-sf",
"-o",
"/dev/null",
"--max-time",
"1",
EMULATE_HEALTH_URL,
]);
return res.code === 0;
}
export function ensureEmulateRunning(): void {
if (emulateReachable()) return;
log("emulate.dev not reachable, spawning daemon");
const res = sh("bash", [START_EMULATE_SH]);
if (res.code !== 0) {
console.error(
`[dw] failed to start emulate daemon:\n${res.stdout}\n${res.stderr}`,
);
}
}
export function killPidFromFile(file: string): boolean {
if (!existsSync(file)) return false;
const pid = Number(readFileSync(file, "utf-8").trim());
if (!pid || Number.isNaN(pid)) return false;
try {
process.kill(pid, "SIGTERM");
} catch {}
rmSync(file, { force: true });
return true;
}
export function killHostProcessByName(name: string): boolean {
const res = sh("pgrep", ["-f", name]);
const pids = res.stdout
.split("\n")
.map((s) => s.trim())
.filter(Boolean)
.filter((s) => /^\d+$/.test(s));
if (pids.length === 0) return false;
for (const pid of pids) {
try {
process.kill(Number(pid), "SIGTERM");
} catch {}
}
return true;
}
export function stopEmulateAndPortless(): void {
const fromPid = killPidFromFile(EMULATE_PID_FILE);
const fromScan = killHostProcessByName("emulate --portless");
if (fromPid || fromScan) log("stopped emulate.dev");
const stop = sh("portless", ["proxy", "stop"]);
if (stop.code === 0) log("stopped portless proxy");
}