Production release flow. Keep it boring and repeatable.
developis the default branch; the team's feature/fix PRs target it. (This is our internal flow — the project doesn't take outside PRs; see CONTRIBUTING.md.)mainis production. A release is a single PR develop → main (norelease/*branch).- After merging to
main, dispatch the protected.github/workflows/supabase-production-deploy.ymlworkflow for that exact commit. Mobile publication is a separate manual run of.github/workflows/release.ymlafter verification.
- Bump the version on
developfirst. Editmobile/app.config.js→expo.version(the marketing version). Open a small PR intodevelopand merge it.- runtimeVersion (
expo.runtimeVersion) is the native-ABI identity. Leave it UNCHANGED for a JS-only release; bump it ONLY on a native change (new native module, URL scheme, permission, config plugin) — that requires a new APK.
- runtimeVersion (
- What's New card — required for every release. Automatically add a nonempty
entry to
mobile/src/data/whatsNew.tsmatching the new version during release preparation. Focus on new features and user-visible improvements: what's new and what's better. Describe real benefits in a few short bullets, not error lists, technical diagnostics, or internal maintenance. Reuse the existing version-based dismissal so the card stays hidden after the user dismisses it. This owner instruction supersedes the earlier features-only policy in issue #97. Permanent owner requirement: every release PR must contain both the app version bump and the matching nonempty What's New entry in its final diff againstmain, even when prepared through a separate PR intodevelop. Before opening or marking it ready, verify both and explicitly list the release version, user-facing highlights and card verification in its description. - Apply pending DB migrations to production (see below) before opening the release PR.
- Open the release PR develop → main. It must pass the required "Migrations applied check" and get its approval, then merge.
- After the merge, dispatch Deploy Supabase API and worker to production on
the exact current
maincommit and approve itsproduction-schemagate. It verifies the actual schema, deploys both Edge Functions, checks that the worker rejects unsigned calls, and checks API/health. Keep generation gates unchanged during deployment, then verify the release-specific API, scheduled-job, and client smoke checks. A merge alone does not deploy them. - If this release changes
runtimeVersion, build a compatible preview APK from the exact newmaincommit before starting Release:cd mobile && eas build --platform android --profile preview --non-interactive --wait. Install it on a spare device; the preview and production APKs use the same Android application ID, so they cannot coexist on one device. This build is what lets the owner test the preview OTA at the second approval gate. Keep the production APK on the regular device until that gate is approved. - Open GitHub Actions → Release → Run workflow, select main, and enter
the full 40-character main commit SHA in
backend_revisionand the successful protected deployment Actions run ID inproduction_deploy_run_id. The workflow checks that the deployment ran from that exact currentmainSHA, rejects an existing version tag before publishing, and verifies the actual schema. It publishes preview OTA first, then waits at a secondproduction-schemaapproval. Test the compatible preview build on a real device and confirm the effective production API/Auth configuration before approving that gate. - After the second approval, the workflow rechecks current
main, publishes production OTA, creates the version tag/GitHub Release, and dispatches the native-gated APK build. A changed nativeruntimeVersionrequires a new APK; existing older-runtime installations cannot receive the new runtime's OTA. Do not merge another release while publication is running. The standalone EAS Update workflow publishes preview only; production uses this release path.
The deploy does not auto-migrate. Files in backend/migrations/*.sql must be run
by hand against the production Supabase DB before deploying code that uses them.
For each migration not yet applied to prod:
- Run it — Supabase → SQL Editor (paste the file), or
psql "$DIRECT_URL" -f backend/migrations/<file>(DIRECT_URL= the 5432 session pooler). - Verify the resulting production objects and behavior. Only then add its
filename to
backend/migrations/applied.txtondevelop. Keep already applied SQL immutable. The protected actual-schema check remains required.
.github/workflows/migrations.yml fails on main and on release PRs if any migration
isn't listed in applied.txt. That required filename check proves only that the
operator recorded it. The protected production Edge deployment and Release
schema jobs inspect actual database objects; follow
SCHEMA_VERIFICATION.md to configure those gates.
The Release workflow requires the protected check before publishing. Migrations
are immutable once applied — never edit an applied file; add a new one.
Installed apps update over the air on next launch — no reinstall. The stable APK download link in the README changes only on a native (runtimeVersion) release.
The Edge API needs its public Supabase publishable/anon key to render the
password-reset page. In production Supabase Auth, allow the exact
/functions/v1/api/confirmed and /functions/v1/api/reset-password URLs on
the production project origin. Verify both pages are deployed before using
them as email redirect or Site URLs. See AUTH_REDIRECTS.md.
The mobile API address comes from EAS EXPO_PUBLIC_API_BASE_URL; changing an
Edge Function or repository file alone does not update already-published mobile
bundles. Confirm the target URL and redirect allowlist in each environment.
An endpoint-only operational update reuses the existing release's source and
native runtime. It does not create another version tag or a new release PR.
For a normal version release, all version/What's New requirements above still
apply. Do not rerun release.yml for an already-published version to repoint its
API: OTA publication runs before that workflow rejects a duplicate version tag,
so the run can partially publish and then fail.
After protected API/worker deployment, recovery configuration and candidate
testing, obtain the owner's approval of a specific production endpoint change.
Use a clean checkout of the verified current main revision, install its locked
dependencies, and verify EXPO_PUBLIC_API_BASE_URL in each EAS environment. Check
the effective runtime and app version before export; do not use a feature branch
or bump native runtime merely to move the API. Recheck remote main immediately
before publication and stop if it differs from the verified checkout/deployment.
Publish the candidate with the preview environment first, using a compatible
preview installation for validation. Once approved, the explicit EAS commands
from that checkout's mobile/ directory are:
eas update --channel preview --environment preview --message "Verify API endpoint" --non-interactive
# Only after candidate validation and approval of the production switch:
eas update --channel production --environment production --message "Update API endpoint" --non-interactiveRecord the source SHA, environment URL, runtime, update group, channel and device results in the release record. Do not imply all users have updated when an upload succeeds. Account for older runtimes, offline clients, fresh installs, recovery links and rollback. Environment-variable rollback alone does not change already-published bundles.