From 1c1864fd882379bc6f929a3b7f1753d91734a991 Mon Sep 17 00:00:00 2001 From: Alan Daniel Date: Sat, 18 Apr 2026 16:14:42 -0400 Subject: [PATCH 1/3] feat(dashboard): local wrangler.dev.jsonc, prod deploy workflow - Use wrangler.dev.jsonc for Vite dev/Vitest; keep wrangler.jsonc for build/preview - Gitignore dev/deploy wrangler files; remove tracked wrangler.jsonc (use example) - Default local D1 migrations to wrangler.dev.jsonc when present - Add generate-wrangler-dashboard-config.mjs and production GHA deploy --- .../workflows/dashboard-deploy-production.yml | 67 +++++++++++++++ apps/dashboard/.gitignore | 5 ++ apps/dashboard/README.md | 4 +- apps/dashboard/package.json | 3 +- apps/dashboard/vite.config.ts | 57 +++++++++---- apps/dashboard/wrangler.jsonc | 56 ------------- apps/dashboard/wrangler.jsonc.example | 20 +++++ .../generate-wrangler-dashboard-config.mjs | 82 +++++++++++++++++++ scripts/run-d1-migrations.mjs | 74 +++++++++++------ 9 files changed, 266 insertions(+), 102 deletions(-) create mode 100644 .github/workflows/dashboard-deploy-production.yml delete mode 100644 apps/dashboard/wrangler.jsonc create mode 100644 scripts/generate-wrangler-dashboard-config.mjs diff --git a/.github/workflows/dashboard-deploy-production.yml b/.github/workflows/dashboard-deploy-production.yml new file mode 100644 index 0000000..12836cd --- /dev/null +++ b/.github/workflows/dashboard-deploy-production.yml @@ -0,0 +1,67 @@ +name: Dashboard — production deploy + +on: + push: + branches: [main] + paths: + - "apps/dashboard/**" + - "scripts/generate-wrangler-dashboard-config.mjs" + - "scripts/run-d1-migrations.mjs" + - ".github/workflows/dashboard-deploy-production.yml" + workflow_dispatch: + +concurrency: + group: dashboard-production + cancel-in-progress: false + +jobs: + deploy: + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - uses: actions/checkout@v4 + + - uses: pnpm/action-setup@v4 + with: + version: 10 + + - uses: actions/setup-node@v4 + with: + node-version: 22 + cache: pnpm + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Generate Wrangler config + run: node scripts/generate-wrangler-dashboard-config.mjs apps/dashboard/wrangler.deploy.json + env: + WRANGLER_COMPATIBILITY_DATE: ${{ vars.WRANGLER_COMPATIBILITY_DATE }} + DASHBOARD_PROD_WORKER_NAME: ${{ secrets.DASHBOARD_PROD_WORKER_NAME }} + DASHBOARD_PROD_D1_DATABASE_NAME: ${{ secrets.DASHBOARD_PROD_D1_DATABASE_NAME }} + DASHBOARD_PROD_D1_DATABASE_ID: ${{ secrets.DASHBOARD_PROD_D1_DATABASE_ID }} + DASHBOARD_PROD_KV_ID: ${{ secrets.DASHBOARD_PROD_KV_ID }} + DASHBOARD_PROD_KV_PREVIEW_ID: ${{ secrets.DASHBOARD_PROD_KV_PREVIEW_ID }} + DASHBOARD_PROD_R2_BUCKET: ${{ secrets.DASHBOARD_PROD_R2_BUCKET }} + DASHBOARD_PROD_R2_PREVIEW_BUCKET: ${{ secrets.DASHBOARD_PROD_R2_PREVIEW_BUCKET }} + + # @cloudflare/vite-plugin resolves `wrangler.jsonc` during `vite build` (deploy:ci). + - name: Stage Wrangler config for Vite + working-directory: apps/dashboard + run: cp wrangler.deploy.json wrangler.jsonc + + - name: Apply D1 migrations (remote) + working-directory: apps/dashboard + env: + WRANGLER_CONFIG: wrangler.deploy.json + CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + run: node ../../scripts/run-d1-migrations.mjs DB --remote + + - name: Build and deploy Worker + working-directory: apps/dashboard + env: + CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + run: pnpm run deploy:ci diff --git a/apps/dashboard/.gitignore b/apps/dashboard/.gitignore index ba79147..ced2922 100644 --- a/apps/dashboard/.gitignore +++ b/apps/dashboard/.gitignore @@ -12,3 +12,8 @@ dist-ssr .vinxi __unconfig* todos.json + +# Local Wrangler config (copy from wrangler.jsonc.example) and CI-generated deploy configs +wrangler.jsonc +wrangler.dev.jsonc +wrangler.deploy.json diff --git a/apps/dashboard/README.md b/apps/dashboard/README.md index bf36c4d..52cf099 100644 --- a/apps/dashboard/README.md +++ b/apps/dashboard/README.md @@ -33,9 +33,9 @@ Copy `.dev.vars.example` to `.dev.vars` and fill in the real values. GitHub comm ## Cloudflare Wrangler Configuration -The live `wrangler.jsonc` is ignored by git, so copy `wrangler.jsonc.example` to `wrangler.jsonc` before you deploy or connect to Cloudflare resources locally. +Copy `wrangler.jsonc.example` to **`wrangler.dev.jsonc`** (gitignored). `pnpm dev` and local D1 migrations use that file so local bindings stay separate from deploy config. For a local production build or `wrangler deploy` without `--config`, you can also maintain **`wrangler.jsonc`** (gitignored). -Fill in the Cloudflare-specific values in `wrangler.jsonc`: +Fill in the Cloudflare-specific values: - `account_id` - `d1_databases[].database_id` diff --git a/apps/dashboard/package.json b/apps/dashboard/package.json index 25f551b..697e519 100644 --- a/apps/dashboard/package.json +++ b/apps/dashboard/package.json @@ -18,7 +18,8 @@ "migrate": "pnpm run migrate:local", "migrate:local": "node ../../scripts/run-d1-migrations.mjs DB --local", "migrate:remote": "node ../../scripts/run-d1-migrations.mjs DB --remote", - "deploy": "pnpm run build && wrangler deploy" + "deploy": "pnpm run build && wrangler deploy", + "deploy:ci": "pnpm run build && wrangler deploy --config wrangler.deploy.json" }, "dependencies": { "@cloudflare/vite-plugin": "^1.31.2", diff --git a/apps/dashboard/vite.config.ts b/apps/dashboard/vite.config.ts index 9f79744..8066c0d 100644 --- a/apps/dashboard/vite.config.ts +++ b/apps/dashboard/vite.config.ts @@ -12,10 +12,29 @@ import { } from "../../scripts/shared-worktree-paths.mjs"; const dashboardRoot = new URL(".", import.meta.url); +const wranglerDevPath = new URL("./wrangler.dev.jsonc", dashboardRoot); +const wranglerMainPath = new URL("./wrangler.jsonc", dashboardRoot); const worktreePersistState = isWorktreeCheckout(dashboardRoot) ? { persistState: { path: getSharedWranglerStatePath(dashboardRoot) } } : {}; +/** Vite dev / Vitest use a dedicated file so local bindings stay out of CI `wrangler.jsonc`. */ +function resolveCloudflareWranglerConfigPath( + command: string, + mode: string, +): string | undefined { + if (command !== "serve" || mode === "production") { + return undefined; + } + if (existsSync(wranglerDevPath)) { + return "wrangler.dev.jsonc"; + } + if (existsSync(wranglerMainPath)) { + return "wrangler.jsonc"; + } + return undefined; +} + function getDevVarFromFile(key: string) { const devVarsPath = new URL("./.dev.vars", dashboardRoot); if (!existsSync(devVarsPath)) { @@ -148,22 +167,26 @@ export default {};`; const useCloudflareRemoteBindings = process.env.CI !== "true" && process.env.VITEST !== "true"; -const config = defineConfig(({ command }) => ({ - server: command === "serve" ? getTunnelServerConfig() : undefined, - plugins: [ - devtools(), - shikiSSRStub(), - betterAuthDialectStub(), - cloudflare({ - viteEnvironment: { name: "ssr" }, - ...worktreePersistState, - remoteBindings: useCloudflareRemoteBindings, - }), - tsconfigPaths({ projects: ["./tsconfig.json"] }), - tailwindcss(), - tanstackStart(), - viteReact(), - ], -})); +const config = defineConfig(({ command, mode }) => { + const wranglerConfigPath = resolveCloudflareWranglerConfigPath(command, mode); + return { + server: command === "serve" ? getTunnelServerConfig() : undefined, + plugins: [ + devtools(), + shikiSSRStub(), + betterAuthDialectStub(), + cloudflare({ + viteEnvironment: { name: "ssr" }, + ...worktreePersistState, + remoteBindings: useCloudflareRemoteBindings, + ...(wranglerConfigPath ? { configPath: wranglerConfigPath } : {}), + }), + tsconfigPaths({ projects: ["./tsconfig.json"] }), + tailwindcss(), + tanstackStart(), + viteReact(), + ], + }; +}); export default config; diff --git a/apps/dashboard/wrangler.jsonc b/apps/dashboard/wrangler.jsonc deleted file mode 100644 index be90a9b..0000000 --- a/apps/dashboard/wrangler.jsonc +++ /dev/null @@ -1,56 +0,0 @@ -{ - "$schema": "node_modules/wrangler/config-schema.json", - "name": "diffkit", - "compatibility_date": "2025-09-02", - "compatibility_flags": [ - "nodejs_compat", - "no_handle_cross_request_promise_resolution" - ], - "main": "src/entry-worker.ts", - "observability": { - "logs": { - "enabled": true, - "invocation_logs": true - }, - "traces": { - "enabled": false - } - }, - "d1_databases": [ - { - "binding": "DB", - "database_name": "diffkit-db", - "database_id": "7a94a843-0906-416c-9908-4c9fe5339db7", - "migrations_dir": "drizzle" - } - ], - "durable_objects": { - "bindings": [ - { - "name": "SIGNAL_RELAY", - "class_name": "SignalRelay" - } - ] - }, - "migrations": [ - { - "tag": "v1", - "new_classes": ["SignalRelay"] - } - ], - "kv_namespaces": [ - { - "binding": "GITHUB_CACHE_KV", - "id": "2e46aa8dd4554072a64e1aad955d6adc", - "preview_id": "373842a94f694ce48f5f5376ab622b18" - } - ], - "r2_buckets": [ - { - "binding": "COMMENT_MEDIA", - "bucket_name": "diffkit-comment-media", - "preview_bucket_name": "diffkit-comment-media-preview", - "remote": true - } - ] -} diff --git a/apps/dashboard/wrangler.jsonc.example b/apps/dashboard/wrangler.jsonc.example index 4e2c234..5c703b6 100644 --- a/apps/dashboard/wrangler.jsonc.example +++ b/apps/dashboard/wrangler.jsonc.example @@ -1,3 +1,23 @@ +// Local setup: +// cp wrangler.jsonc.example wrangler.dev.jsonc +// Vite dev / Vitest load `wrangler.dev.jsonc` (gitignored). Fill in placeholders there. +// +// Optional — only if you run `vite build` or `wrangler deploy` without `--config`: +// cp wrangler.jsonc.example wrangler.jsonc +// +// GitHub Actions (production) secrets — map into generated `wrangler.deploy.json`: +// CLOUDFLARE_API_TOKEN +// CLOUDFLARE_ACCOUNT_ID +// DASHBOARD_PROD_WORKER_NAME +// DASHBOARD_PROD_D1_DATABASE_NAME +// DASHBOARD_PROD_D1_DATABASE_ID +// DASHBOARD_PROD_KV_ID +// DASHBOARD_PROD_KV_PREVIEW_ID (optional; defaults to DASHBOARD_PROD_KV_ID) +// DASHBOARD_PROD_R2_BUCKET +// DASHBOARD_PROD_R2_PREVIEW_BUCKET +// +// Optional repository variables: +// WRANGLER_COMPATIBILITY_DATE { "$schema": "node_modules/wrangler/config-schema.json", "name": "diffkit", diff --git a/scripts/generate-wrangler-dashboard-config.mjs b/scripts/generate-wrangler-dashboard-config.mjs new file mode 100644 index 0000000..376e725 --- /dev/null +++ b/scripts/generate-wrangler-dashboard-config.mjs @@ -0,0 +1,82 @@ +import { writeFileSync } from "node:fs"; +import { resolve } from "node:path"; + +const [, , outArg] = process.argv; + +if (!outArg) { + console.error( + "Usage: node scripts/generate-wrangler-dashboard-config.mjs " + ); + process.exit(1); +} + +const outFile = resolve(outArg); +const compatibilityDate = + process.env.WRANGLER_COMPATIBILITY_DATE || "2025-09-02"; + +const name = process.env.DASHBOARD_PROD_WORKER_NAME; +const d1Name = process.env.DASHBOARD_PROD_D1_DATABASE_NAME; +const d1Id = process.env.DASHBOARD_PROD_D1_DATABASE_ID; +const kvId = process.env.DASHBOARD_PROD_KV_ID; +const kvPreviewId = + process.env.DASHBOARD_PROD_KV_PREVIEW_ID || process.env.DASHBOARD_PROD_KV_ID; +const r2Bucket = process.env.DASHBOARD_PROD_R2_BUCKET; +const r2Preview = process.env.DASHBOARD_PROD_R2_PREVIEW_BUCKET; + +for (const [key, val] of Object.entries({ + DASHBOARD_PROD_WORKER_NAME: name, + DASHBOARD_PROD_D1_DATABASE_NAME: d1Name, + DASHBOARD_PROD_D1_DATABASE_ID: d1Id, + DASHBOARD_PROD_KV_ID: kvId, + DASHBOARD_PROD_R2_BUCKET: r2Bucket, + DASHBOARD_PROD_R2_PREVIEW_BUCKET: r2Preview, +})) { + if (!val) { + console.error(`Missing required environment variable: ${key}`); + process.exit(1); + } +} + +const config = { + $schema: "node_modules/wrangler/config-schema.json", + compatibility_date: compatibilityDate, + compatibility_flags: [ + "nodejs_compat", + "no_handle_cross_request_promise_resolution", + ], + main: "src/entry-worker.ts", + observability: { + logs: { enabled: true, invocation_logs: true }, + traces: { enabled: false }, + }, + durable_objects: { + bindings: [{ name: "SIGNAL_RELAY", class_name: "SignalRelay" }], + }, + migrations: [{ tag: "v1", new_classes: ["SignalRelay"] }], + name, + d1_databases: [ + { + binding: "DB", + database_name: d1Name, + database_id: d1Id, + migrations_dir: "drizzle", + }, + ], + kv_namespaces: [ + { + binding: "GITHUB_CACHE_KV", + id: kvId, + preview_id: kvPreviewId, + }, + ], + r2_buckets: [ + { + binding: "COMMENT_MEDIA", + bucket_name: r2Bucket, + preview_bucket_name: r2Preview, + remote: true, + }, + ], +}; + +writeFileSync(outFile, `${JSON.stringify(config, null, 2)}\n`, "utf8"); diff --git a/scripts/run-d1-migrations.mjs b/scripts/run-d1-migrations.mjs index ecd9e4a..89643b7 100644 --- a/scripts/run-d1-migrations.mjs +++ b/scripts/run-d1-migrations.mjs @@ -1,54 +1,76 @@ import { spawnSync } from "node:child_process"; -import { getSharedWranglerStatePath, isWorktreeCheckout } from "./shared-worktree-paths.mjs"; +import { existsSync } from "node:fs"; +import { resolve } from "node:path"; +import { + getSharedWranglerStatePath, + isWorktreeCheckout, +} from "./shared-worktree-paths.mjs"; + +const PNPM_SCRIPT_EXT = /\.(c|m)?js$/u; const databaseName = process.argv[2]; const mode = process.argv[3] ?? "--local"; if (!databaseName) { - console.error( - "Usage: node scripts/run-d1-migrations.mjs [--local|--remote]", - ); - process.exit(1); + console.error( + "Usage: node scripts/run-d1-migrations.mjs [--local|--remote]" + ); + process.exit(1); } if (mode !== "--local" && mode !== "--remote") { - console.error(`Unsupported mode "${mode}". Use --local or --remote.`); - process.exit(1); + console.error(`Unsupported mode "${mode}". Use --local or --remote.`); + process.exit(1); +} + +const args = ["exec", "wrangler"]; + +let wranglerConfig = process.env.WRANGLER_CONFIG; +if (!wranglerConfig && mode === "--local") { + const cwd = process.cwd(); + if (existsSync(resolve(cwd, "wrangler.dev.jsonc"))) { + wranglerConfig = "wrangler.dev.jsonc"; + } else if (existsSync(resolve(cwd, "wrangler.jsonc"))) { + wranglerConfig = "wrangler.jsonc"; + } +} +if (wranglerConfig) { + args.push("-c", wranglerConfig); } -const args = ["exec", "wrangler", "d1", "migrations", "apply", databaseName, mode]; +args.push("d1", "migrations", "apply", databaseName, mode); if (mode === "--local" && isWorktreeCheckout()) { - args.push("--persist-to", getSharedWranglerStatePath()); + args.push("--persist-to", getSharedWranglerStatePath()); } function runPnpm(commandArgs) { - const pnpmExecPath = process.env.npm_execpath; + const pnpmExecPath = process.env.npm_execpath; - if (pnpmExecPath) { - const shouldUseNode = /\.(c|m)?js$/u.test(pnpmExecPath); - const command = shouldUseNode ? process.execPath : pnpmExecPath; - const args = shouldUseNode ? [pnpmExecPath, ...commandArgs] : commandArgs; + if (pnpmExecPath) { + const shouldUseNode = PNPM_SCRIPT_EXT.test(pnpmExecPath); + const command = shouldUseNode ? process.execPath : pnpmExecPath; + const args = shouldUseNode ? [pnpmExecPath, ...commandArgs] : commandArgs; - return spawnSync(command, args, { - stdio: "inherit", - shell: process.platform === "win32" && !shouldUseNode, - }); - } + return spawnSync(command, args, { + stdio: "inherit", + shell: process.platform === "win32" && !shouldUseNode, + }); + } - const pnpmCommand = process.platform === "win32" ? "pnpm.cmd" : "pnpm"; + const pnpmCommand = process.platform === "win32" ? "pnpm.cmd" : "pnpm"; - return spawnSync(pnpmCommand, commandArgs, { - stdio: "inherit", - shell: process.platform === "win32", - }); + return spawnSync(pnpmCommand, commandArgs, { + stdio: "inherit", + shell: process.platform === "win32", + }); } const result = runPnpm(args); if (result.error) { - console.error(result.error.message); - process.exit(1); + console.error(result.error.message); + process.exit(1); } process.exit(result.status ?? 0); From 4da31631a5d30f1070e13a194187f97592f4a816 Mon Sep 17 00:00:00 2001 From: Alan Daniel Date: Sat, 18 Apr 2026 16:20:21 -0400 Subject: [PATCH 2/3] refactor: share resolveWranglerConfigPath for Vite and D1 migrations --- apps/dashboard/vite.config.ts | 28 +++++++----------------- scripts/resolve-wrangler-config-path.mjs | 23 +++++++++++++++++++ scripts/run-d1-migrations.mjs | 14 +++++------- 3 files changed, 37 insertions(+), 28 deletions(-) create mode 100644 scripts/resolve-wrangler-config-path.mjs diff --git a/apps/dashboard/vite.config.ts b/apps/dashboard/vite.config.ts index 8066c0d..aca64a6 100644 --- a/apps/dashboard/vite.config.ts +++ b/apps/dashboard/vite.config.ts @@ -1,4 +1,5 @@ import { existsSync, readFileSync } from "node:fs"; +import { fileURLToPath } from "node:url"; import { cloudflare } from "@cloudflare/vite-plugin"; import tailwindcss from "@tailwindcss/vite"; import { devtools } from "@tanstack/devtools-vite"; @@ -6,35 +7,18 @@ 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 { resolveWranglerConfigPath } from "../../scripts/resolve-wrangler-config-path.mjs"; import { getSharedWranglerStatePath, isWorktreeCheckout, } from "../../scripts/shared-worktree-paths.mjs"; const dashboardRoot = new URL(".", import.meta.url); -const wranglerDevPath = new URL("./wrangler.dev.jsonc", dashboardRoot); -const wranglerMainPath = new URL("./wrangler.jsonc", dashboardRoot); +const dashboardRootDir = fileURLToPath(dashboardRoot); const worktreePersistState = isWorktreeCheckout(dashboardRoot) ? { persistState: { path: getSharedWranglerStatePath(dashboardRoot) } } : {}; -/** Vite dev / Vitest use a dedicated file so local bindings stay out of CI `wrangler.jsonc`. */ -function resolveCloudflareWranglerConfigPath( - command: string, - mode: string, -): string | undefined { - if (command !== "serve" || mode === "production") { - return undefined; - } - if (existsSync(wranglerDevPath)) { - return "wrangler.dev.jsonc"; - } - if (existsSync(wranglerMainPath)) { - return "wrangler.jsonc"; - } - return undefined; -} - function getDevVarFromFile(key: string) { const devVarsPath = new URL("./.dev.vars", dashboardRoot); if (!existsSync(devVarsPath)) { @@ -168,7 +152,11 @@ const useCloudflareRemoteBindings = process.env.CI !== "true" && process.env.VITEST !== "true"; const config = defineConfig(({ command, mode }) => { - const wranglerConfigPath = resolveCloudflareWranglerConfigPath(command, mode); + const wranglerConfigPath = resolveWranglerConfigPath({ + command, + mode, + rootDir: dashboardRootDir, + }); return { server: command === "serve" ? getTunnelServerConfig() : undefined, plugins: [ diff --git a/scripts/resolve-wrangler-config-path.mjs b/scripts/resolve-wrangler-config-path.mjs new file mode 100644 index 0000000..22abdf0 --- /dev/null +++ b/scripts/resolve-wrangler-config-path.mjs @@ -0,0 +1,23 @@ +import { existsSync } from "node:fs"; +import path from "node:path"; + +/** + * Same resolution Vite uses for the Cloudflare plugin in dev / Vitest (`serve` + non-production). + * + * @param {{ command: string; mode: string; rootDir: string }} opts + * @returns {string | undefined} Relative filename for `wrangler -c`, or undefined for default discovery. + */ +export function resolveWranglerConfigPath({ command, mode, rootDir }) { + if (command !== "serve" || mode === "production") { + return undefined; + } + const dev = path.join(rootDir, "wrangler.dev.jsonc"); + const main = path.join(rootDir, "wrangler.jsonc"); + if (existsSync(dev)) { + return "wrangler.dev.jsonc"; + } + if (existsSync(main)) { + return "wrangler.jsonc"; + } + return undefined; +} diff --git a/scripts/run-d1-migrations.mjs b/scripts/run-d1-migrations.mjs index 89643b7..a4837fa 100644 --- a/scripts/run-d1-migrations.mjs +++ b/scripts/run-d1-migrations.mjs @@ -1,6 +1,5 @@ import { spawnSync } from "node:child_process"; -import { existsSync } from "node:fs"; -import { resolve } from "node:path"; +import { resolveWranglerConfigPath } from "./resolve-wrangler-config-path.mjs"; import { getSharedWranglerStatePath, isWorktreeCheckout, @@ -27,12 +26,11 @@ const args = ["exec", "wrangler"]; let wranglerConfig = process.env.WRANGLER_CONFIG; if (!wranglerConfig && mode === "--local") { - const cwd = process.cwd(); - if (existsSync(resolve(cwd, "wrangler.dev.jsonc"))) { - wranglerConfig = "wrangler.dev.jsonc"; - } else if (existsSync(resolve(cwd, "wrangler.jsonc"))) { - wranglerConfig = "wrangler.jsonc"; - } + wranglerConfig = resolveWranglerConfigPath({ + command: "serve", + mode: "development", + rootDir: process.cwd(), + }); } if (wranglerConfig) { args.push("-c", wranglerConfig); From 7d7fc13949167c8dcba4d146842b737cda90b79c Mon Sep 17 00:00:00 2001 From: Alan Daniel Date: Sat, 18 Apr 2026 17:40:45 -0400 Subject: [PATCH 3/3] fix(types): use resolve-wrangler-config-path.mts for tsc; strip-types for migrations --- .../workflows/dashboard-deploy-production.yml | 2 +- .lintstagedrc.json | 2 +- apps/dashboard/package.json | 4 +-- apps/dashboard/tsconfig.json | 6 +++- apps/dashboard/vite.config.ts | 2 +- scripts/link-worktree-dev-vars.mjs | 28 +++++++++++++------ ...h.mjs => resolve-wrangler-config-path.mts} | 10 +++++-- scripts/run-d1-migrations.mjs | 2 +- 8 files changed, 37 insertions(+), 19 deletions(-) rename scripts/{resolve-wrangler-config-path.mjs => resolve-wrangler-config-path.mts} (66%) diff --git a/.github/workflows/dashboard-deploy-production.yml b/.github/workflows/dashboard-deploy-production.yml index 12836cd..4386358 100644 --- a/.github/workflows/dashboard-deploy-production.yml +++ b/.github/workflows/dashboard-deploy-production.yml @@ -57,7 +57,7 @@ jobs: WRANGLER_CONFIG: wrangler.deploy.json CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} - run: node ../../scripts/run-d1-migrations.mjs DB --remote + run: node --experimental-strip-types ../../scripts/run-d1-migrations.mjs DB --remote - name: Build and deploy Worker working-directory: apps/dashboard diff --git a/.lintstagedrc.json b/.lintstagedrc.json index fb39e9d..60e2dea 100644 --- a/.lintstagedrc.json +++ b/.lintstagedrc.json @@ -1,5 +1,5 @@ { - "*.{js,jsx,ts,tsx,json,jsonc,css,scss,md,mdx}": [ + "*.{js,jsx,ts,mts,tsx,json,jsonc,css,scss,md,mdx}": [ "biome check --write --no-errors-on-unmatched" ] } diff --git a/apps/dashboard/package.json b/apps/dashboard/package.json index 697e519..2dbd1e8 100644 --- a/apps/dashboard/package.json +++ b/apps/dashboard/package.json @@ -16,8 +16,8 @@ "check": "biome check", "check-types": "tsc --noEmit", "migrate": "pnpm run migrate:local", - "migrate:local": "node ../../scripts/run-d1-migrations.mjs DB --local", - "migrate:remote": "node ../../scripts/run-d1-migrations.mjs DB --remote", + "migrate:local": "node --experimental-strip-types ../../scripts/run-d1-migrations.mjs DB --local", + "migrate:remote": "node --experimental-strip-types ../../scripts/run-d1-migrations.mjs DB --remote", "deploy": "pnpm run build && wrangler deploy", "deploy:ci": "pnpm run build && wrangler deploy --config wrangler.deploy.json" }, diff --git a/apps/dashboard/tsconfig.json b/apps/dashboard/tsconfig.json index 8184a9b..5ded190 100644 --- a/apps/dashboard/tsconfig.json +++ b/apps/dashboard/tsconfig.json @@ -1,6 +1,10 @@ { "extends": "@diffkit/typescript-config/start-app.json", - "include": ["**/*.ts", "**/*.tsx"], + "include": [ + "**/*.ts", + "**/*.tsx", + "../../scripts/resolve-wrangler-config-path.mts" + ], "compilerOptions": { "baseUrl": ".", "paths": { diff --git a/apps/dashboard/vite.config.ts b/apps/dashboard/vite.config.ts index aca64a6..ad0742b 100644 --- a/apps/dashboard/vite.config.ts +++ b/apps/dashboard/vite.config.ts @@ -7,7 +7,7 @@ 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 { resolveWranglerConfigPath } from "../../scripts/resolve-wrangler-config-path.mjs"; +import { resolveWranglerConfigPath } from "../../scripts/resolve-wrangler-config-path.mts"; import { getSharedWranglerStatePath, isWorktreeCheckout, diff --git a/scripts/link-worktree-dev-vars.mjs b/scripts/link-worktree-dev-vars.mjs index d97daab..e29d52d 100644 --- a/scripts/link-worktree-dev-vars.mjs +++ b/scripts/link-worktree-dev-vars.mjs @@ -1,11 +1,14 @@ // When this repo is opened through Git worktrees, only the main checkout usually -// keeps the real `.dev.vars` file. This script links that file into each worktree -// so every Conductor workspace sees the same local env vars during development. +// keeps the real `.dev.vars` and `wrangler.dev.jsonc`. This script links those files +// into each worktree so every workspace sees the same local config during development. import { execSync } from "node:child_process"; import fs from "node:fs/promises"; import path from "node:path"; -const REQUIRED_ENV_PATHS = ["apps/dashboard/.dev.vars"]; +const PRIMARY_WORKTREE_LINK_PATHS = [ + "apps/dashboard/.dev.vars", + "apps/dashboard/wrangler.dev.jsonc", +]; function safeExec(command, cwd) { return execSync(command, { cwd, stdio: ["ignore", "pipe", "pipe"] }) @@ -24,7 +27,10 @@ async function fileExists(filePath) { function resolveRoots() { const repoRoot = safeExec("git rev-parse --show-toplevel", process.cwd()); - const commonDirRaw = safeExec("git rev-parse --git-common-dir", process.cwd()); + const commonDirRaw = safeExec( + "git rev-parse --git-common-dir", + process.cwd() + ); const commonDir = path.isAbsolute(commonDirRaw) ? commonDirRaw : path.resolve(repoRoot, commonDirRaw); @@ -33,7 +39,7 @@ function resolveRoots() { return { repoRoot, primaryRoot }; } -async function linkDevVars(relPath, primaryRoot, repoRoot) { +async function linkFromPrimaryWorktree(relPath, primaryRoot, repoRoot) { const source = path.join(primaryRoot, relPath); const target = path.join(repoRoot, relPath); const targetDir = path.dirname(target); @@ -74,7 +80,7 @@ async function linkDevVars(relPath, primaryRoot, repoRoot) { function printResults(linked, skipped) { if (linked.length > 0) { - console.log("Linked dev vars:"); + console.log("Linked from primary worktree:"); for (const file of linked) { console.log(` - ${file}`); } @@ -98,8 +104,12 @@ async function main() { const linked = []; const skipped = []; - for (const relPath of REQUIRED_ENV_PATHS) { - const result = await linkDevVars(relPath, primaryRoot, repoRoot); + for (const relPath of PRIMARY_WORKTREE_LINK_PATHS) { + const result = await linkFromPrimaryWorktree( + relPath, + primaryRoot, + repoRoot + ); if (result.status === "linked") { linked.push(result.reason); @@ -114,6 +124,6 @@ async function main() { } main().catch((error) => { - console.error("dev vars link failed:", error.message); + console.error("worktree dev file link failed:", error.message); process.exit(1); }); diff --git a/scripts/resolve-wrangler-config-path.mjs b/scripts/resolve-wrangler-config-path.mts similarity index 66% rename from scripts/resolve-wrangler-config-path.mjs rename to scripts/resolve-wrangler-config-path.mts index 22abdf0..86a8dea 100644 --- a/scripts/resolve-wrangler-config-path.mjs +++ b/scripts/resolve-wrangler-config-path.mts @@ -4,10 +4,14 @@ import path from "node:path"; /** * Same resolution Vite uses for the Cloudflare plugin in dev / Vitest (`serve` + non-production). * - * @param {{ command: string; mode: string; rootDir: string }} opts - * @returns {string | undefined} Relative filename for `wrangler -c`, or undefined for default discovery. + * @returns Relative filename for `wrangler -c`, or undefined for default discovery. */ -export function resolveWranglerConfigPath({ command, mode, rootDir }) { +export function resolveWranglerConfigPath(opts: { + command: string; + mode: string; + rootDir: string; +}): string | undefined { + const { command, mode, rootDir } = opts; if (command !== "serve" || mode === "production") { return undefined; } diff --git a/scripts/run-d1-migrations.mjs b/scripts/run-d1-migrations.mjs index a4837fa..faba1ea 100644 --- a/scripts/run-d1-migrations.mjs +++ b/scripts/run-d1-migrations.mjs @@ -1,5 +1,5 @@ import { spawnSync } from "node:child_process"; -import { resolveWranglerConfigPath } from "./resolve-wrangler-config-path.mjs"; +import { resolveWranglerConfigPath } from "./resolve-wrangler-config-path.mts"; import { getSharedWranglerStatePath, isWorktreeCheckout,