Skip to content

Apply stacker Supabase migrations to the hosted project in CI #315

Description

@ckrough

Problem

Nothing in CI applies apps/stacker/supabase/migrations/*.sql to the hosted Supabase project. .github/workflows/deploy.yml runs gated Alembic migrations for retriever (RETRIEVER_DATABASE_URL) and petdata (PETDATA_DATABASE_URL), but deploy-stacker only builds and deploys to Cloudflare Pages. There is no supabase db push step anywhere in .github/workflows/ (grep returns zero supabase matches).

A stacker Supabase migration therefore merges to main and is silently not deployed. The schema change exists in the repo and in local dev (where supabase start applies it), but never reaches the hosted database.

How this surfaced

#288 added apps/stacker/supabase/migrations/20260713000000_custom_access_token_hook.sql, creating public.custom_access_token_hook. PR #289 merged. Enabling the hook in the hosted dashboard (Auth -> Hooks -> Custom Access Token) then failed: the function picker was empty for every schema, because the function had never been created in the hosted database. It only worked after a manual supabase db push from a workstation.

Every future stacker Supabase migration hits this same "merged but not deployed" gap until CI closes it.

Proposed fix

Add a gated supabase db push to the stacker deploy path, following the same ordering contract the backend migrations already use: the migration must succeed before the deploy takes traffic, and a migration failure fails the job.

  • Trigger: merge to main when apps/stacker/supabase/migrations/** changes (the changes job already path-filters apps/stacker/**).
  • Auth: SUPABASE_ACCESS_TOKEN + project ref as repo secrets; supabase link then supabase db push.
  • Ordering: run before the Cloudflare Pages cutover in deploy-stacker, so a failed migration blocks the frontend deploy.
  • Production only, never on PR previews (mirrors the existing deploy-retriever migration gating).

Alternative worth weighing: fold this into the broader hosted-Supabase config-as-code mechanism rather than a standalone step.

Notes

  • apps/stacker/supabase/config.toml sets signing_keys_path = "./signing_keys.json", which makes the CLI abort on any command (including db push) when that file is absent. CI needs the file present (or the setting handled) or the push fails before it starts. The file is gitignored (**/supabase/signing_keys.json) and is local-dev only.
  • Related: Configure production SMTP (Cloudflare Email Service) for Supabase auth emails #157 (production SMTP for Supabase auth emails) covers hosted Supabase auth configuration under the same "hosted config is code, not click-ops" principle. This issue covers schema migrations specifically.

Acceptance Criteria

  • Merging a change under apps/stacker/supabase/migrations/** to main applies it to the hosted Supabase project with no manual step.
  • A failing migration fails the workflow and blocks the stacker Cloudflare Pages cutover.
  • The migration step does not run for PR previews.
  • Required secrets are documented in the deploy.yml header comment alongside the existing *_DATABASE_URL entries.
  • Verified end to end by landing a trivial no-op stacker migration and confirming it appears in the hosted database without manual intervention.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestmodule:stackerStacker portal (apps/stacker)

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions