From c174fe6c814e188b9b8ba131edcd41c45274db97 Mon Sep 17 00:00:00 2001 From: Anuraj Jit Saikia Date: Fri, 19 Jun 2026 00:47:59 +0530 Subject: [PATCH] chore(deploy): auto-deploy Convex backend on merge + fail loud on misconfig MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Root cause of the FunctionPathNotFound outage: M11 backend functions were merged but never deployed to the dev deployment the website silently falls back to, so the client called functions the backend didn't have and the WebSocket entered a reconnect storm. Backend deploys were manual and decoupled from merges, so they were forgotten (same gap as M6.2 and M7). - .github/workflows/convex-deploy.yml — on push to main touching convex/, run `convex deploy` to prod (needs repo secret CONVEX_DEPLOY_KEY). Code and the deployment can no longer drift. - deployment.ts — a production build with no VITE_CONVEX_URL still falls back to dev, but now logs a loud error instead of silently pointing prod users at the dev backend. - CLAUDE.md — document the dev (tangible-finch-68) vs prod (formal-dove-357) model and the deploy rules. --- .github/workflows/convex-deploy.yml | 33 +++++++++++++++++++++++++++++ CLAUDE.md | 14 ++++++++++++ apps/web/src/lib/deployment.ts | 24 +++++++++++++++++++-- 3 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/convex-deploy.yml diff --git a/.github/workflows/convex-deploy.yml b/.github/workflows/convex-deploy.yml new file mode 100644 index 0000000..fd0cc4b --- /dev/null +++ b/.github/workflows/convex-deploy.yml @@ -0,0 +1,33 @@ +name: Deploy Convex backend +# The recurring outage (M6.2, M7, and the FunctionPathNotFound storm) all came +# from the same gap: backend code was merged but never deployed, so the client +# called functions the deployment didn't have. This couples a backend deploy to +# every merge into main, so code and deployment can never drift again. +# +# Setup (one-time): create a PROD deploy key at +# dashboard.convex.dev → khata → prod (formal-dove-357) → Settings → Deploy Keys +# and add it as the repo secret CONVEX_DEPLOY_KEY +# (Settings → Secrets and variables → Actions → New repository secret). +on: + push: + branches: + - main + paths: + - "convex/**" + - ".github/workflows/convex-deploy.yml" +jobs: + deploy: + name: convex deploy (prod) + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@v4 + - uses: oven-sh/setup-bun@v2 + with: + bun-version: latest + - name: Install dependencies + run: bun install --frozen-lockfile + - name: Deploy functions to the prod deployment + env: + CONVEX_DEPLOY_KEY: ${{ secrets.CONVEX_DEPLOY_KEY }} + run: bunx convex deploy -y diff --git a/CLAUDE.md b/CLAUDE.md index 25db56b..7a7c701 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -30,6 +30,20 @@ - `better-auth` session storage uses the browser's default cookie/localStorage — no SecureStore or in-memory hydration needed on web. - Tailwind CSS v4 — use `@import "tailwindcss"` in CSS, no `tailwind.config.js`. Theme tokens live in `src/index.css` as CSS custom properties. +## Convex deployments (dev vs prod) — read this before touching the backend + +There are **two** Convex backends: + +- **dev** — `tangible-finch-68` (`.env.local` → `CONVEX_DEPLOYMENT=dev:tangible-finch-68`). Used by local `bun run dev`. Deploy to it with `bunx convex dev --once`. +- **prod** — `formal-dove-357`. Used by the released app. The APK build pins it (`build-apk.yml` sets `VITE_CONVEX_URL`). The web app should also point here in production via `VITE_CONVEX_URL`. + +**Code in `convex/` does not run until it is deployed to a deployment.** The frontend's `_generated/api` reflects local code; if the client calls a function the *deployment* hasn't received, the WebSocket drops with `FunctionPathNotFound` and every live query stalls through reconnect backoff. This has bitten the project repeatedly (M6.2, M7, the June 2026 outage where M11 functions were merged but never deployed, so the website — silently on dev — broke). + +**Rules:** +- A merge to `main` auto-deploys the backend to **prod** via `.github/workflows/convex-deploy.yml` (needs repo secret `CONVEX_DEPLOY_KEY`). Don't rely on remembering to deploy by hand. +- The production web build **must** set `VITE_CONVEX_URL` to the prod URL. `deployment.ts` falls back to dev only as a last resort and logs a loud error in prod builds if it has to. +- After changing `convex/` and testing on dev, the durable path is to merge → auto-deploy. For a hotfix you can `bunx convex deploy` (prod) manually, but that's the exception. + ## Release and versioning - Release automation is managed by `release-please` via `.github/workflows/release-please.yml`. diff --git a/apps/web/src/lib/deployment.ts b/apps/web/src/lib/deployment.ts index a713947..8637092 100644 --- a/apps/web/src/lib/deployment.ts +++ b/apps/web/src/lib/deployment.ts @@ -1,8 +1,28 @@ +// Which Convex backend this build talks to. +// +// IMPORTANT: a *production* build must set VITE_CONVEX_URL to the production +// deployment. If it doesn't, we fall back to the DEV deployment below — which is +// how the live website silently ended up talking to the dev backend (and hit +// FunctionPathNotFound when dev was behind on deploys). So in a prod build with +// no explicit URL we now scream in the console instead of failing silently. +// +// Local dev (`bun run dev`) reads VITE_CONVEX_URL from apps/web/.env.local, or +// falls back to dev here — which is correct for local work. const DEV_URL = "https://tangible-finch-68.convex.cloud"; const DEV_SITE_URL = "https://tangible-finch-68.convex.site"; -export const CONVEX_URL = - (import.meta.env.VITE_CONVEX_URL as string | undefined) ?? DEV_URL; +const explicitUrl = import.meta.env.VITE_CONVEX_URL as string | undefined; + +if (import.meta.env.PROD && !explicitUrl) { + // eslint-disable-next-line no-console + console.error( + "[khata] VITE_CONVEX_URL is not set in this PRODUCTION build — falling back " + + "to the DEV Convex deployment. Set VITE_CONVEX_URL to the prod deployment " + + "(formal-dove-357) in the hosting environment so the live app uses prod." + ); +} + +export const CONVEX_URL = explicitUrl ?? DEV_URL; export const CONVEX_SITE_URL = (import.meta.env.VITE_CONVEX_SITE_URL as string | undefined) ??