diff --git a/package.json b/package.json index 331b202ba..66aa6c827 100644 --- a/package.json +++ b/package.json @@ -85,6 +85,8 @@ "dw:identify": "ENV_FILE=.env infisical run --env=dev --recursive -- bun scripts/dw/index.ts identify", "dw:setup": "ENV_FILE=.env infisical run --env=dev --recursive -- bun scripts/dw/index.ts setup", "dw:run": "ENV_FILE=.env infisical run --env=dev --recursive -- bun scripts/dw/index.ts run", + "dw:enable": "bun scripts/dw/index.ts enable", + "dw:disable": "bun scripts/dw/index.ts disable", "d": "ENV_FILE=.env infisical run --env=dev --recursive -- bun scripts/dev.ts", "d:prod": "ENV_FILE=.env infisical run --env=dev --recursive -- bun scripts/dev.ts --production", "dx": "bun scripts/dx.ts", diff --git a/scripts/dw/README.md b/scripts/dw/README.md index e94cbe91c..9bd7a09f9 100644 --- a/scripts/dw/README.md +++ b/scripts/dw/README.md @@ -34,6 +34,8 @@ bun dw logs # tail last 2000 lines without attaching bun dw reset # nuke DB branch + containers → re-provision bun dw teardown # full cleanup of this worktree bun dw teardown --all # full cleanup of every agent worktree +bun dw disable # rename .env.local -> .env.local.disabled (fall back to canonical env) +bun dw enable # rename .env.local.disabled -> .env.local ``` ## Subcommands @@ -113,6 +115,20 @@ Attaches your terminal to the worktree's tmux session. Detach with `Ctrl+B` then bun dw attach ``` +### `bun dw disable` +Renames each managed `.env.local` to `.env.local.disabled` so tooling falls back to the canonical (infisical-injected) env. Useful when a test or script needs the canonical env without nuking your worktree provisioning. + +```sh +bun dw disable +``` + +### `bun dw enable` +Inverse of `disable` — restores each `.env.local.disabled` to `.env.local`. + +```sh +bun dw enable +``` + ### `bun dw teardown` Full cleanup of the current worktree: deletes Neon branch, unregisters portless aliases, kills tmux session, removes Docker compose stack, removes `.env.local` files, and deletes the registry entry. If no other agent worktrees remain, also stops emulate + portless daemons. diff --git a/scripts/dw/commands/disable.ts b/scripts/dw/commands/disable.ts new file mode 100644 index 000000000..66f8c213f --- /dev/null +++ b/scripts/dw/commands/disable.ts @@ -0,0 +1,9 @@ +import { log } from "../helpers/shell.ts"; +import { disableEnvLocalFiles } from "../helpers/env-files.ts"; + +export function cmdDisable(): void { + const { moved, missing, alreadyDisabled } = disableEnvLocalFiles(); + log( + `disable: ${moved} renamed, ${alreadyDisabled} already disabled, ${missing} not present`, + ); +} diff --git a/scripts/dw/commands/enable.ts b/scripts/dw/commands/enable.ts new file mode 100644 index 000000000..1af9b3c01 --- /dev/null +++ b/scripts/dw/commands/enable.ts @@ -0,0 +1,9 @@ +import { log } from "../helpers/shell.ts"; +import { enableEnvLocalFiles } from "../helpers/env-files.ts"; + +export function cmdEnable(): void { + const { moved, missing, alreadyEnabled } = enableEnvLocalFiles(); + log( + `enable: ${moved} restored, ${alreadyEnabled} already enabled, ${missing} not present`, + ); +} diff --git a/scripts/dw/constants.ts b/scripts/dw/constants.ts index 9956274fa..4c9d037fd 100644 --- a/scripts/dw/constants.ts +++ b/scripts/dw/constants.ts @@ -25,3 +25,5 @@ export const ENV_LOCAL_TARGETS = [ "vite/.env.local", "apps/checkout/.env.local", ] as const; + +export const ENV_LOCAL_DISABLED_SUFFIX = ".disabled"; diff --git a/scripts/dw/helpers/env-files.ts b/scripts/dw/helpers/env-files.ts index 858c61566..e203b158a 100644 --- a/scripts/dw/helpers/env-files.ts +++ b/scripts/dw/helpers/env-files.ts @@ -1,10 +1,10 @@ -import { existsSync, readFileSync, writeFileSync, rmSync } from "node:fs"; +import { existsSync, readFileSync, renameSync, writeFileSync, rmSync } from "node:fs"; import { dirname, join } from "node:path"; import { homedir } from "node:os"; import { log } from "./shell.ts"; import { forceSslVerifyFull } from "./url.ts"; import { aliasesFor, dragonflyPortFor, elasticMqPortFor } from "./ports.ts"; -import { PROJECT_ROOT, ENV_LOCAL_TARGETS } from "../constants.ts"; +import { PROJECT_ROOT, ENV_LOCAL_TARGETS, ENV_LOCAL_DISABLED_SUFFIX } from "../constants.ts"; import type { RegistryEntry } from "../types.ts"; // Simple KEY=VALUE parse (no quoting/multiline). Sufficient for .env.local @@ -121,5 +121,52 @@ export function removeEnvLocalFiles(): void { rmSync(abs, { force: true }); log(`removed ${rel}`); } + const disabled = `${abs}${ENV_LOCAL_DISABLED_SUFFIX}`; + if (existsSync(disabled)) { + rmSync(disabled, { force: true }); + log(`removed ${rel}${ENV_LOCAL_DISABLED_SUFFIX}`); + } } } + +export function disableEnvLocalFiles(): { moved: number; missing: number; alreadyDisabled: number } { + let moved = 0; + let missing = 0; + let alreadyDisabled = 0; + for (const rel of ENV_LOCAL_TARGETS) { + const abs = join(PROJECT_ROOT, rel); + const disabled = `${abs}${ENV_LOCAL_DISABLED_SUFFIX}`; + if (existsSync(abs)) { + if (existsSync(disabled)) rmSync(disabled, { force: true }); + renameSync(abs, disabled); + log(`disabled ${rel} -> ${rel}${ENV_LOCAL_DISABLED_SUFFIX}`); + moved++; + } else if (existsSync(disabled)) { + alreadyDisabled++; + } else { + missing++; + } + } + return { moved, missing, alreadyDisabled }; +} + +export function enableEnvLocalFiles(): { moved: number; missing: number; alreadyEnabled: number } { + let moved = 0; + let missing = 0; + let alreadyEnabled = 0; + for (const rel of ENV_LOCAL_TARGETS) { + const abs = join(PROJECT_ROOT, rel); + const disabled = `${abs}${ENV_LOCAL_DISABLED_SUFFIX}`; + if (existsSync(disabled)) { + if (existsSync(abs)) rmSync(abs, { force: true }); + renameSync(disabled, abs); + log(`enabled ${rel}`); + moved++; + } else if (existsSync(abs)) { + alreadyEnabled++; + } else { + missing++; + } + } + return { moved, missing, alreadyEnabled }; +} diff --git a/scripts/dw/index.ts b/scripts/dw/index.ts index b4382a734..e65323e95 100644 --- a/scripts/dw/index.ts +++ b/scripts/dw/index.ts @@ -8,6 +8,8 @@ import { cmdReset } from "./commands/reset.ts"; import { cmdLogs } from "./commands/logs.ts"; import { cmdAttach } from "./commands/attach.ts"; import { cmdIdentify } from "./commands/identify.ts"; +import { cmdEnable } from "./commands/enable.ts"; +import { cmdDisable } from "./commands/disable.ts"; async function main(): Promise { const sub = process.argv[2]; @@ -40,9 +42,15 @@ async function main(): Promise { case "identify": cmdIdentify(); break; + case "enable": + cmdEnable(); + break; + case "disable": + cmdDisable(); + break; default: fatal( - `unknown subcommand: ${sub} (use: setup | run | teardown | list | reset | logs | attach | identify)`, + `unknown subcommand: ${sub} (use: setup | run | teardown | list | reset | logs | attach | identify | enable | disable)`, ); } } diff --git a/vite/vite.config.ts b/vite/vite.config.ts index 494f528f2..9d03373dd 100644 --- a/vite/vite.config.ts +++ b/vite/vite.config.ts @@ -5,6 +5,12 @@ import react from "@vitejs/plugin-react"; import { defineConfig, type Plugin } from "vite"; import tsconfigPaths from "vite-tsconfig-paths"; +// Defaults so the app works when no .env.local is present +// (e.g. after `bun dw disable`). Real values from .env / infisical / +// process.env still take precedence. +process.env.VITE_BACKEND_URL ||= "http://localhost:8080"; +process.env.VITE_FRONTEND_URL ||= "http://localhost:3000"; + function printPortlessUrl(): Plugin { return { name: "print-portless-url",