Mobile: OTA updates on merge + build info screen
Status summary: implemented, checks green (typecheck, tests, oxlint, knip, expo export). Remaining: merge, then install the CI-triggered preview build once and rebuild the dev client (new native modules).
The workflow Misha wants: flit freely between the Metro-connected dev client (agent working on the app, laptop nearby) and a standalone preview build (real usage, laptop off). Both builds overwrite each other (same bundle ID) — that's fine. The missing piece: the preview build should track main automatically, without a 15-minute native rebuild per merge.
Approach#
EAS Update. The preview binary embeds expo-updates pointed at the preview
channel; a Depot CI job on every merge to main publishes the JS bundle
(eas update --channel preview, seconds). Installed preview apps pull the
new JS on next launch. Native builds happen only when the project fingerprint
changes — CI detects that (no existing preview build with the update's
runtime version) and kicks off eas build --profile preview --no-wait.
EXPO_TOKEN lives in Doppler _shared (inherited by os/prd), readable in
CI via the existing DOPPLER_TOKEN.
Checklist#
- expo-updates config
expo install expo-updates expo-application+eas update:configure, then switched runtimeVersion policy from its appVersion default to fingerprint (appVersion would let native-module merges silently strand old binaries); channels per profile in eas.json - build stamping
apps/mobile/scripts/write-build-info.mjs→src/build-info.json(checked-in all-empty placeholder); runs via CI publish script and theeas-build-pre-installhook; also replaces the hand-maintained BUILD_TIMESTAMP insrc/lib/build-info.ts - Build info screen
src/app/build-info.tsx: bundle branch/commit/builder/time, update channel/runtime/id/published, app version/native build/install time, check-for-update-now button (guarded byUpdates.isEnabledso Metro dev shows a note instead) - Drawer item "Build info" in
project-drawer.tsx, below Switch project - CI workflow
.depot/workflows/mobile-eas-update.yml→scripts/ci/publish-mobile-update.ts; concurrency-serialized so updates publish in commit order; triggers a native preview build when no NEW/IN_QUEUE/IN_PROGRESS/FINISHED preview build matches the published runtime version - README "Dev ↔ preview: two builds, one phone" section
- typecheck / test / lint / knip green plus
expo export --platform iosas a bundle smoke
Post-merge notes#
- First CI run will publish an update, find no preview build for the new fingerprint (expo-updates + expo-application are new native modules), and trigger one. Install it from its EAS page — that's the last manual preview install until the next native change.
- The dev client needs
pnpm --dir apps/mobile build:development:iosfor the same reason. - The preview build Misha installed on 2026-08-03 (pre-expo-updates) can never receive OTA updates; it gets replaced by the above.