The mobile app builds on Expo Application Services and ships JavaScript/UI changes over the air. Native changes require a new APK/AAB; everything else reaches installed phones without a reinstall.
- Channels — every build is pinned to a channel (
development,preview,productionin eas.json).eas update --channel Xpublishes only to builds from channel X, so production phones never receive preview code. - Runtime version — pinned explicitly in app.config.js, decoupled
from the marketing
version(which release PRs bump freely). An update is only delivered to builds with the sameruntimeVersion. BumpruntimeVersionby hand whenever you add/remove a native module or change native config, then build and distribute a new APK — that is what stops an OTA update from landing on a binary that lacks the native code it needs. - Environment variables —
mobile/.envis gitignored and never reaches EAS servers. Builds readEXPO_PUBLIC_*from EAS environment variables (one set per environment: development / preview / production). Manage them witheas envor the expo.dev dashboard.
cd mobile
npm i -g eas-cli # or use npx eas-cli
eas login
eas init # links the project and reports the projectId
eas update:configure # reports updates.url (https://u.expo.dev/<projectId>)Config lives in a dynamic app.config.js (there is no app.json), which the
EAS CLI cannot write to. eas init / eas update:configure therefore print
the projectId and updates.url for you to paste into app.config.js by
hand rather than editing a file. (This project's values are already set — this
section is only for standing up a brand-new EAS project.)
Then create the environment variables for each environment (values from
.env.example; the Supabase anon key is public by design, but the production
API URL should be your deployed backend, not a LAN IP):
eas env:create --environment production --name EXPO_PUBLIC_API_BASE_URL
eas env:create --environment production --name EXPO_PUBLIC_SUPABASE_URL
eas env:create --environment production --name EXPO_PUBLIC_SUPABASE_ANON_KEY
# repeat with --environment preview / development as neededcd mobile
eas build --platform android --profile preview # produces an installable APKWhen it finishes, open the build page link (or expo.dev → project → builds) on
your phone and install the APK directly, or scan the QR code the CLI prints.
The production profile builds an AAB for Play Store submission; use
preview for sideloading.
cd mobile
npm run update:production # eas update --channel production
npm run update:preview # eas update --channel previewInstalled apps fetch the update on next launch (checkAutomatically: ON_LOAD).
OTA cannot ship native code. Instead:
- Bump
expo.versionin app.config.js (e.g. 1.0.0 → 1.1.0), and setruntimeVersionto match the new build. eas build --platform android --profile production(and/orpreview).- Reinstall the new binary on devices; subsequent OTA updates target the new runtime version.
Two workflows in .github/workflows, both need the
EXPO_TOKEN repository secret (expo.dev → Account settings → Access tokens;
add at GitHub → Settings → Secrets and variables → Actions):
- eas-update.yml — every push to
maintouchingmobile/**publishes an OTA update to the production channel automatically. Manual dispatch lets you pickpreviewinstead. - eas-build.yml — manual dispatch only (builds cost quota); choose the profile.