diff --git a/apps/dashboard/package.json b/apps/dashboard/package.json index 8f95859..924a1ca 100644 --- a/apps/dashboard/package.json +++ b/apps/dashboard/package.json @@ -15,7 +15,7 @@ "lint": "biome lint", "check": "biome check", "check-types": "tsc --noEmit", - "migrate": "wrangler d1 migrations apply quickhub-db --local", + "migrate": "node ../../scripts/run-d1-migrations.mjs quickhub-db", "deploy": "pnpm run build && wrangler deploy" }, "dependencies": { diff --git a/apps/dashboard/vite.config.ts b/apps/dashboard/vite.config.ts index 9fa1393..6955f50 100644 --- a/apps/dashboard/vite.config.ts +++ b/apps/dashboard/vite.config.ts @@ -7,11 +7,23 @@ import { tanstackStart } from "@tanstack/react-start/plugin/vite"; import viteReact from "@vitejs/plugin-react"; import { defineConfig } from "vite"; import tsconfigPaths from "vite-tsconfig-paths"; +import { + getSharedWranglerStatePath, + isWorktreeCheckout, +} from "../../scripts/shared-worktree-paths.mjs"; + +const dashboardRoot = new URL(".", import.meta.url); +const worktreePersistState = isWorktreeCheckout(dashboardRoot) + ? { persistState: { path: getSharedWranglerStatePath(dashboardRoot) } } + : {}; const config = defineConfig({ plugins: [ devtools(), - cloudflare({ viteEnvironment: { name: "ssr" } }), + cloudflare({ + viteEnvironment: { name: "ssr" }, + ...worktreePersistState, + }), tsconfigPaths({ projects: ["./tsconfig.json"] }), tailwindcss(), tanstackStart(), diff --git a/scripts/run-d1-migrations.mjs b/scripts/run-d1-migrations.mjs new file mode 100644 index 0000000..8598546 --- /dev/null +++ b/scripts/run-d1-migrations.mjs @@ -0,0 +1,26 @@ +import { spawnSync } from "node:child_process"; +import { getSharedWranglerStatePath, isWorktreeCheckout } from "./shared-worktree-paths.mjs"; + +const databaseName = process.argv[2]; + +if (!databaseName) { + console.error("Usage: node scripts/run-d1-migrations.mjs "); + process.exit(1); +} + +const args = ["exec", "wrangler", "d1", "migrations", "apply", databaseName, "--local"]; + +if (isWorktreeCheckout()) { + args.push("--persist-to", getSharedWranglerStatePath()); +} + +const result = spawnSync("pnpm", args, { + stdio: "inherit", +}); + +if (result.error) { + console.error(result.error.message); + process.exit(1); +} + +process.exit(result.status ?? 0); diff --git a/scripts/shared-worktree-paths.d.mts b/scripts/shared-worktree-paths.d.mts new file mode 100644 index 0000000..0ec56c2 --- /dev/null +++ b/scripts/shared-worktree-paths.d.mts @@ -0,0 +1,4 @@ +export function getRepoRoot(cwd?: string | URL): string; +export function getPrimaryWorktreeRoot(cwd?: string | URL): string; +export function isWorktreeCheckout(cwd?: string | URL): boolean; +export function getSharedWranglerStatePath(cwd?: string | URL): string; diff --git a/scripts/shared-worktree-paths.mjs b/scripts/shared-worktree-paths.mjs new file mode 100644 index 0000000..6961658 --- /dev/null +++ b/scripts/shared-worktree-paths.mjs @@ -0,0 +1,36 @@ +import { execSync } from "node:child_process"; +import path from "node:path"; + +function safeExec(command, cwd = process.cwd()) { + return execSync(command, { cwd, stdio: ["ignore", "pipe", "pipe"] }) + .toString() + .trim(); +} + +export function getRepoRoot(cwd = process.cwd()) { + return safeExec("git rev-parse --show-toplevel", cwd); +} + +export function getPrimaryWorktreeRoot(cwd = process.cwd()) { + const repoRoot = getRepoRoot(cwd); + const commonDirRaw = safeExec("git rev-parse --git-common-dir", cwd); + const commonDir = path.isAbsolute(commonDirRaw) + ? commonDirRaw + : path.resolve(repoRoot, commonDirRaw); + + return path.dirname(commonDir); +} + +export function isWorktreeCheckout(cwd = process.cwd()) { + return path.resolve(getRepoRoot(cwd)) !== path.resolve(getPrimaryWorktreeRoot(cwd)); +} + +export function getSharedWranglerStatePath(cwd = process.cwd()) { + return path.join(getPrimaryWorktreeRoot(cwd), ".wrangler", "state"); +} + +if (process.argv[1] && import.meta.url === new URL(process.argv[1], "file:").href) { + if (isWorktreeCheckout()) { + console.log(getSharedWranglerStatePath()); + } +}