Skip to content

Branching & deployment

Two shared Supabase projects:

ProjectPurpose
movement-mapping-devShared dev project. CI applies merged migrations here.
movement-mapping-prodProduction. Only CI applies migrations here.

Project-level provisioning (not covered by migrations)

Section titled “Project-level provisioning (not covered by migrations)”

supabase/migrations/*.sql only reaches the database. A handful of settings live at the Supabase project level instead, and a brand-new project (or a recreated one) starts without them:

  • PostgREST’s exposed-schema list defaults to public, graphql_publicapp and network are invisible to the API until added, regardless of RLS.
  • The Auth site_url defaults to http://localhost:3000 on a new project — it’s what {{ .SiteURL }} resolves to in Auth email templates (confirmation, magic link), so without setting it, confirmation emails link back to localhost no matter what environment sent them.
  • The Auth redirect allow-list starts empty, so /auth/confirm and /auth/callback are rejected as invalid redirect targets.

scripts/supa-configure-project.sh [project-ref] sets all three (idempotent), and runs automatically from scripts/supa-first-run.sh whenever a project has no migrations yet — so this covers the local shared/parent project by default. It must also be run once against movement-mapping-dev and movement-mapping-prod when each is first provisioned, since CI only ever applies migrations there, never this script. Run it from a checkout with no local .env (or a temporarily moved-aside one) — the script re-sources .env if present, which silently overrides SUPABASE_PROJECT_REF/SITE_URL passed on the command line:

Terminal window
SUPABASE_PROJECT_REF=<dev-or-prod-ref> SITE_URL=<its-public-url> \
./scripts/supa-configure-project.sh

One more setting needs a decision before it can be scripted: Auth email template customisation (the “Confirm signup” and “Magic Link” templates) is blocked by Supabase on the free tier without a custom SMTP provider — without it, both templates keep the default {{ .ConfirmationURL }} link instead of the token_hash-based link /auth/confirm expects, so email confirmation and magic-link sign-in silently fail (you land back on /setup or /login logged out).

This project uses Resend as the custom SMTP provider. Once you have a domain verified in Resend and an API key, set RESEND_API_KEY and SMTP_SENDER_EMAIL (optionally SMTP_SENDER_NAME) and re-run scripts/supa-configure-project.sh — it configures Resend as the project’s SMTP relay and pushes the branded templates from supabase/templates/ (confirmation.html, magic-link.html) via the Management API. Without those variables set, the script skips this step and prints the template text to set by hand instead (Dashboard → Authentication → Email Templates).

Deployment is config-as-code in render.yaml, with two services pinned to the EU (Frankfurt) region:

  • a web service for apps/app (the TanStack Start Node server), and
  • a static site for apps/docs (built and published from apps/docs/dist).

There is no manual dashboard configuration; secrets are set in Render’s environment, not committed. See Environment variables.

  • .github/workflows/ci.yml — on PRs and pushes to main: frozen-lockfile install, biome check, typecheck, build of both apps, and a Socket.dev supply-chain check.
  • .github/workflows/db.yml — applies migrations to movement-mapping-dev on merge to main, and promotes them to movement-mapping-prod on release.
  • .github/workflows/supabase-branch-cleanup.yml — deletes a git branch’s Supabase branch when the git branch is deleted.