From 014003b2d4585c5912f718984b61c9a2f795e779 Mon Sep 17 00:00:00 2001 From: Alan Daniel Date: Tue, 7 Apr 2026 19:15:32 -0400 Subject: [PATCH 1/2] Share local D1 state across worktrees --- apps/dashboard/package.json | 2 +- apps/dashboard/vite.config.ts | 10 +++++++++- scripts/shared-worktree-paths.d.mts | 3 +++ scripts/shared-worktree-paths.mjs | 30 +++++++++++++++++++++++++++++ 4 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 scripts/shared-worktree-paths.d.mts create mode 100644 scripts/shared-worktree-paths.mjs diff --git a/apps/dashboard/package.json b/apps/dashboard/package.json index 8f95859..35f452b 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": "wrangler d1 migrations apply quickhub-db --local --persist-to \"$(node ../../scripts/shared-worktree-paths.mjs)\"", "deploy": "pnpm run build && wrangler deploy" }, "dependencies": { diff --git a/apps/dashboard/vite.config.ts b/apps/dashboard/vite.config.ts index 9fa1393..5b3aec9 100644 --- a/apps/dashboard/vite.config.ts +++ b/apps/dashboard/vite.config.ts @@ -7,11 +7,19 @@ 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 } from "../../scripts/shared-worktree-paths.mjs"; + +const sharedStatePath = getSharedWranglerStatePath( + new URL(".", import.meta.url), +); const config = defineConfig({ plugins: [ devtools(), - cloudflare({ viteEnvironment: { name: "ssr" } }), + cloudflare({ + viteEnvironment: { name: "ssr" }, + persistState: { path: sharedStatePath }, + }), tsconfigPaths({ projects: ["./tsconfig.json"] }), tailwindcss(), tanstackStart(), diff --git a/scripts/shared-worktree-paths.d.mts b/scripts/shared-worktree-paths.d.mts new file mode 100644 index 0000000..8909b3f --- /dev/null +++ b/scripts/shared-worktree-paths.d.mts @@ -0,0 +1,3 @@ +export function getRepoRoot(cwd?: string | URL): string; +export function getPrimaryWorktreeRoot(cwd?: string | URL): string; +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..ce84906 --- /dev/null +++ b/scripts/shared-worktree-paths.mjs @@ -0,0 +1,30 @@ +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 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) { + console.log(getSharedWranglerStatePath()); +} From b682759832e927e8d52dbf54bd62f02815291ed4 Mon Sep 17 00:00:00 2001 From: Alan Daniel Date: Tue, 7 Apr 2026 19:17:54 -0400 Subject: [PATCH 2/2] Only share local D1 state in worktrees --- apps/dashboard/package.json | 2 +- apps/dashboard/vite.config.ts | 14 +++++++++----- scripts/run-d1-migrations.mjs | 26 ++++++++++++++++++++++++++ scripts/shared-worktree-paths.d.mts | 1 + scripts/shared-worktree-paths.mjs | 8 +++++++- 5 files changed, 44 insertions(+), 7 deletions(-) create mode 100644 scripts/run-d1-migrations.mjs diff --git a/apps/dashboard/package.json b/apps/dashboard/package.json index 35f452b..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 --persist-to \"$(node ../../scripts/shared-worktree-paths.mjs)\"", + "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 5b3aec9..6955f50 100644 --- a/apps/dashboard/vite.config.ts +++ b/apps/dashboard/vite.config.ts @@ -7,18 +7,22 @@ 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 } from "../../scripts/shared-worktree-paths.mjs"; +import { + getSharedWranglerStatePath, + isWorktreeCheckout, +} from "../../scripts/shared-worktree-paths.mjs"; -const sharedStatePath = getSharedWranglerStatePath( - new URL(".", import.meta.url), -); +const dashboardRoot = new URL(".", import.meta.url); +const worktreePersistState = isWorktreeCheckout(dashboardRoot) + ? { persistState: { path: getSharedWranglerStatePath(dashboardRoot) } } + : {}; const config = defineConfig({ plugins: [ devtools(), cloudflare({ viteEnvironment: { name: "ssr" }, - persistState: { path: sharedStatePath }, + ...worktreePersistState, }), tsconfigPaths({ projects: ["./tsconfig.json"] }), tailwindcss(), 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 index 8909b3f..0ec56c2 100644 --- a/scripts/shared-worktree-paths.d.mts +++ b/scripts/shared-worktree-paths.d.mts @@ -1,3 +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 index ce84906..6961658 100644 --- a/scripts/shared-worktree-paths.mjs +++ b/scripts/shared-worktree-paths.mjs @@ -21,10 +21,16 @@ export function getPrimaryWorktreeRoot(cwd = process.cwd()) { 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) { - console.log(getSharedWranglerStatePath()); + if (isWorktreeCheckout()) { + console.log(getSharedWranglerStatePath()); + } }