Skip to main content

Build and release

Status: Scaffolded 2026-09-24

Builds happen on EAS. The scaffold ships the profiles, the guard scripts, and the config; the EAS project, the Apple app record, and the Play listing are created once by the maintainer (mobile deployment).

Profiles​

eas.json, all with node: "24":

ProfiledistributiondevelopmentClientautoIncrementenvExtends
developmentinternaltruelocal or dev API URL
previewinternalproduction API URL
productionstoretrueproduction API URL
testflightstoretrueproduction, iOS
playstorestoretrueAndroid app-bundleproduction

cli.appVersionSource is remote: EAS stores the iOS build number and Android version code and increments them on the store profiles. Two people building from the same commit cannot collide, and no version file churns in git.

scripts/check-prod-ready.sh​

Runs grep -rEn over app/, src/, app.config.ts, and eas.json and exits 1 on any match:

PatternCatches
localhost, 127\.0\.0\.1, 10\.0\.2\.2a dev API URL left in a constant or an env block
sk_livea live payment or service key
AKIA[0-9A-Z]{16}an AWS access key id
AIza[0-9A-Za-z_-]{35}a Google API key
\b10\.\d+\.\d+\.\d+\b, \b192\.168\.\d+\.\d+\b, \b172\.(1[6-9]|2\d|3[01])\.\d+\.\d+\bprivate network addresses, which usually means a laptop's LAN address in an API URL

Lines ending in // prod-ready: ignore are excluded, for the one test fixture that needs a string shaped like a key. Adding a pattern is a one-line PR; the script is the single definition of "not ready".

npm run check:prod runs it locally and inside validate.

scripts/eas-build-pre-install.sh​

EAS runs this hook on its build machine before npm install:

case "$EAS_BUILD_PROFILE" in
production|testflight|playstore) bash scripts/check-prod-ready.sh ;;
esac

A hit fails the build in its first seconds, before any credential is used. development and preview builds skip it because they are allowed to point at a local API.

Versions​

  • version in app.config.ts (1.2.0) is the marketing version members see. Bump it by hand, with a chore: release mobile 1.2.0 commit, when a store release is prepared.
  • Build number and version code are remote and automatic. Never set them in app.config.ts.
  • runtimeVersion follows the appVersion policy so an OTA update can never reach a binary with different native code.

Commands​

# development client, once per native change
eas build --profile development --platform ios
eas build --profile development --platform android

# testers, installed from a link
eas build --profile preview --platform all

# stores
eas build --profile testflight --platform ios
eas submit --profile testflight --platform ios --latest # needs ascAppId in eas.json submit profile

eas build --profile playstore --platform android
eas submit --profile playstore --platform android --latest # needs the service-account EAS secret

The submit profiles in eas.json carry placeholders (ascAppId, appleTeamId, serviceAccountKeyPath pointing at an EAS file secret) that the maintainer fills after the one-time store setup. The values are account identifiers, not secrets, but they are still filled in locally rather than in the docs.

Before a store build​

  1. npm run validate green.
  2. A preview build of the same commit installed and exercised on a device: sign in, map, an event, a message.
  3. version bumped and the release page drafted.
  4. Then the store profile build.