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.
105 lines
3.1 KiB
TypeScript
105 lines
3.1 KiB
TypeScript
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
import { join } from "node:path";
|
|
import { sh, fatal, log } from "./shell.ts";
|
|
import { SHARED_DIR, PROJECT_ROOT } from "../constants.ts";
|
|
|
|
export function listSqlFiles(dir: string): string[] {
|
|
const res = Bun.spawnSync(["ls", dir]);
|
|
const stdout = res.stdout ? new TextDecoder().decode(res.stdout).trim() : "";
|
|
if (!stdout) return [];
|
|
return stdout.split("\n").filter((f) => f.endsWith(".sql"));
|
|
}
|
|
|
|
export function writeTempDrizzleConfig(outDir: string): string {
|
|
const tmp = join(SHARED_DIR, `.dw-${process.pid}.config.ts`);
|
|
const content = `import { defineConfig } from "drizzle-kit";\nexport default defineConfig({\n\tdialect: "postgresql",\n\tout: ${JSON.stringify(outDir)},\n\tschema: "./db/schema.ts",\n\tdbCredentials: { url: process.env.DATABASE_URL! },\n});\n`;
|
|
writeFileSync(tmp, content);
|
|
return tmp;
|
|
}
|
|
|
|
export function generateAndApplyMigration(
|
|
branchName: string,
|
|
databaseUrl: string,
|
|
): void {
|
|
const outDir = join(SHARED_DIR, "drizzle-local", branchName);
|
|
if (existsSync(outDir)) rmSync(outDir, { recursive: true, force: true });
|
|
mkdirSync(outDir, { recursive: true });
|
|
|
|
const drizzleConfigPath = writeTempDrizzleConfig(outDir);
|
|
try {
|
|
log(`generating initial migration for ${branchName}`);
|
|
const gen = sh(
|
|
"bunx",
|
|
["drizzle-kit", "generate", "--config", drizzleConfigPath],
|
|
{
|
|
cwd: SHARED_DIR,
|
|
env: {
|
|
...(process.env as Record<string, string>),
|
|
NODE_OPTIONS: "--import tsx",
|
|
},
|
|
},
|
|
);
|
|
if (gen.code !== 0) {
|
|
fatal(`drizzle-kit generate failed:\n${gen.stdout}\n${gen.stderr}`);
|
|
}
|
|
|
|
const sqlFiles = listSqlFiles(outDir);
|
|
if (sqlFiles.length === 0) {
|
|
fatal(`no .sql files generated in ${outDir}`);
|
|
}
|
|
|
|
log(`applying ${sqlFiles.length} migration file(s) to ${branchName}`);
|
|
for (const f of sqlFiles) {
|
|
const p = join(outDir, f);
|
|
const sqlBody = readFileSync(p, "utf-8");
|
|
const mig = sh("psql", [databaseUrl, "-v", "ON_ERROR_STOP=1"], {
|
|
stdin: sqlBody,
|
|
});
|
|
if (mig.code !== 0) {
|
|
fatal(
|
|
`applying migration ${f} failed:\n${mig.stdout}\n${mig.stderr}`,
|
|
);
|
|
}
|
|
}
|
|
} finally {
|
|
if (existsSync(drizzleConfigPath)) rmSync(drizzleConfigPath);
|
|
}
|
|
}
|
|
|
|
export function loadDbFunctions(branchName: string, databaseUrl: string): void {
|
|
log(`loading DB functions into ${branchName}`);
|
|
const sqlDir = join(
|
|
PROJECT_ROOT,
|
|
"server",
|
|
"src",
|
|
"internal",
|
|
"balances",
|
|
"utils",
|
|
"sql",
|
|
);
|
|
const sqlFiles = [
|
|
"deductFromRollovers.sql",
|
|
"deductFromMainBalance.sql",
|
|
"unwindFromLockReceipt.sql",
|
|
"getTotalBalance.sql",
|
|
"deductFromAdditionalBalance.sql",
|
|
"getAvailableOverageFromSpendLimit.sql",
|
|
"performDeduction.sql",
|
|
"syncBalances.sql",
|
|
"syncBalancesV2.sql",
|
|
"resetCusEnts.sql",
|
|
];
|
|
for (const f of sqlFiles) {
|
|
const p = join(sqlDir, f);
|
|
const sqlBody = readFileSync(p, "utf-8");
|
|
const res = sh("psql", [databaseUrl, "-v", "ON_ERROR_STOP=1"], {
|
|
stdin: sqlBody,
|
|
});
|
|
if (res.code !== 0) {
|
|
fatal(
|
|
`loading DB function ${f} into ${branchName} failed:\n${res.stdout}\n${res.stderr}`,
|
|
);
|
|
}
|
|
}
|
|
}
|