Files
cfw-autumn/scripts/dw/helpers/setup.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

80 lines
2.5 KiB
TypeScript

import { log, fatal, shInherit } from "./shell.ts";
import {
ensureTemplateBranch,
createBranch,
connectionString,
findBranchByName,
} from "./neon.ts";
import { generateAndApplyMigration, loadDbFunctions } from "./migration.ts";
import { loadRegistry, saveRegistry } from "./registry.ts";
import { PROJECT_ROOT, NEON_TEMPLATE_BRANCH } from "../constants.ts";
import type { RegistryEntry } from "../types.ts";
export async function setupAgentWorktree(
entry: RegistryEntry,
registry: Record<string, RegistryEntry>,
): Promise<RegistryEntry> {
const { branchName } = entry;
if (!branchName) fatal("entry missing branchName");
// If branch already exists on Neon and we have a URL, just refresh.
if (entry.branchId && findBranchByName(branchName)) {
const url = connectionString(branchName, { pooled: true });
const next: RegistryEntry = {
...entry,
databaseUrl: url,
lastUsedAt: Date.now(),
};
registry[entry.path] = next;
saveRegistry(registry);
return next;
}
// First-run provisioning.
log(`first run for ${branchName} — provisioning neon branch`);
ensureTemplateBranch();
const branch = createBranch(branchName, NEON_TEMPLATE_BRANCH);
// Use direct (non-pooled) URL for DDL; pooler can interfere with some DDL paths.
const directUrl = connectionString(branchName, { pooled: false });
generateAndApplyMigration(branchName, directUrl);
loadDbFunctions(branchName, directUrl);
// Pooled URL for runtime.
const pooledUrl = connectionString(branchName, { pooled: true });
const next: RegistryEntry = {
...entry,
branchId: branch.id,
databaseUrl: pooledUrl,
lastUsedAt: Date.now(),
};
registry[entry.path] = next;
saveRegistry(registry);
return next;
}
// Auto-seed unit test org into the per-worktree Neon branch.
// setup-test is idempotent so we always invoke it on dw default;
// failures are non-fatal (log + continue) since downstream dev
// might still be useful without the seed.
export async function autoSetupTestOrg(entry: RegistryEntry): Promise<void> {
if (!entry.databaseUrl) {
log("autoSetupTestOrg: no databaseUrl on entry, skipping");
return;
}
log(`seeding unit test org in ${entry.branchName ?? "worktree"}`);
const code = shInherit(
"bun",
["scripts/setup/setup-test.ts", "--yes"],
{
cwd: PROJECT_ROOT,
env: {
...(process.env as Record<string, string>),
DATABASE_URL: entry.databaseUrl,
DATABASE_CRITICAL_URL: entry.databaseUrl,
},
},
);
if (code !== 0) {
console.error(`[dw] setup-test exited with code ${code}; continuing`);
}
}