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.
66 lines
1.6 KiB
TypeScript
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");
|
|
}
|