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).- Merging to
maintriggers Railway backend deployment. 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.
- On merge, Railway auto-deploys the API from
main. Keep generation paused during backend/worker transitions. Verify the new main commit is deployed to the API and workers,/healthsucceeds, and the release-specific smoke checks pass. Do not resume old generators against the new editorial schema. - Open GitHub Actions → Release → Run workflow, select main, and enter
the full 40-character main commit SHA you verified on production API/workers
in
backend_revision. This is an operator attestation; the workflow does not inspect Railway deployments itself. Do not submit it until checks are complete. - The workflow first requires protected actual-schema verification (see setup and checks). It rejects another branch, an older deployment or a main revision that advanced before validation. After those gates, it publishes production + preview OTA, cuts the version tag/GitHub Release, and dispatches the native-gated APK build. 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, or the app queries columns/tables that
don't exist (this caused the pool-topup "column claimed_at does not exist" crash).
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). - Add its filename to
backend/migrations/applied.txt.
.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 main-only protected schema workflow and Railway
pre-deploy check inspect actual database objects; follow
SCHEMA_VERIFICATION.md to configure both 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.
SUPABASE_ANON_KEYon the Railwayapiservice — the/reset-passwordpage needs it.- Supabase → Auth → Redirect URLs must include
<api-domain>/reset-password.
Follow the Railway migration runbook for service
handover, recovery redirects, device checks and old-endpoint retirement. The
mobile API address comes from EAS EXPO_PUBLIC_API_BASE_URL; editing Railway
or merging the runbook alone does not update installed applications.
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 backend/worker revision verification, recovery configuration and candidate
testing, obtain the owner's approval of the specific production endpoint switch.
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/deployments.
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 Railway API migration" --non-interactive
# Only after candidate validation and approval of the production switch:
eas update --channel production --environment production --message "Migrate Railway API endpoint" --non-interactiveRecord the source SHA, environment URL, runtime, update group, channel and device results in the migration PR. Do not imply all users have updated when an upload succeeds. Retain the old API until older runtimes, offline clients, fresh installs, recovery links and rollback are accounted for. Environment-variable rollback alone does not change already-published bundles.