Native Release¶
The Tomoda native iOS and Android apps are built locally from a developer's machine and submitted to the App Store / Google Play by hand. The build process pulls Sentry secrets from GCP Secret Manager at build time via the wrapper script at frontend/scripts/build-with-secrets.sh — see Local build (current default) below.
This page covers the iOS + Android flow at the conceptual level. For portal-side management of each store, see Play Store and App Store. For local dev environment setup, see Local development.
EAS is documented as the alternative path
The sections below cover EAS (Expo Application Services) — Expo's hosted-build platform — as an alternative we may adopt later. Tomoda doesn't have an EAS account today; you can ignore the EAS-specific instructions until that decision is revisited.
Local build (current default)¶
cd frontend
# iOS release build
./scripts/build-with-secrets.sh ios
# Android release build
./scripts/build-with-secrets.sh android
# Regenerate native projects before a release (if app.config.js changed)
./scripts/build-with-secrets.sh prebuild
The script pulls two values from GCP Secret Manager — tomoda-sentry-dsn and tomoda-sentry-auth-token — into environment variables for the build process and execs the corresponding expo command. No secret values are written to disk. See scripts.md for the full reference.
After the build completes, upload the resulting .ipa to TestFlight via Transporter (or xcrun altool) and the .aab / .apk to Google Play Console manually. The store-side workflow is covered in App Store and Play Store.
EAS-based build (alternative, not adopted)¶
Build profiles¶
The build matrix is defined in frontend/eas.json:
| Profile | Use for | Distribution | Notes |
|---|---|---|---|
development-simulator |
Local iOS simulator dev | internal | developmentClient: true, ios.simulator: true |
development |
EAS dev client on a real device | internal | developmentClient: true |
preview |
Internal QA / TestFlight beta / Play internal track | internal | Stand-alone build, debug-stripped |
production |
App Store + Google Play release | store | autoIncrement: true on version code; production env vars baked in |
Production env vars¶
The production profile pins two EXPO_PUBLIC_* values directly in eas.json:
"production": {
"autoIncrement": true,
"env": {
"EXPO_PUBLIC_API_URL": "https://api.tomoda.life/api/v1",
"EXPO_PUBLIC_WS_URL": "wss://api.tomoda.life/ws"
}
}
Other EXPO_PUBLIC_* values (the Google OAuth client IDs in particular) come from the EAS Secret store or the developer's environment at build time. As with the web frontend, anything EXPO_PUBLIC_* is shipped to clients — never put a real secret behind one. See Secrets.
App version source¶
cli.appVersionSource: "remote" means EAS — not app.json — is the source of truth for the version code / build number. autoIncrement: true on the production profile means EAS bumps the build number on each production build. Marketing version (1.2.3) still comes from app.json.
Building¶
cd frontend
# Local sim build, instant
eas build --profile development-simulator --platform ios
# Real-device dev client (iOS + Android in parallel)
eas build --profile development --platform all
# Internal preview / TestFlight / Play internal
eas build --profile preview --platform all
# Production
eas build --profile production --platform all
Builds run on EAS infrastructure; you get a link to monitor progress and download the artifact. For production builds you'll also want to make sure you've run the test suite locally first (task test — see Testing).
Submitting¶
Once a production build is done, push it to the stores:
# iOS — App Store Connect
eas submit --profile production --platform ios
# Android — Google Play
eas submit --profile production --platform android
The submit configuration is at the bottom of eas.json:
"submit": {
"production": {
"ios": { "ascAppId": "6765908734" }
}
}
This is the App Store Connect numeric app ID for tomoda — eas submit --platform ios uploads the latest production build to that record.
iOS — TestFlight workflow¶
eas build --profile production --platform iosfinishes and uploads to App Store Connect.- Apple processes the build (~5–30 min).
- In App Store Connect → TestFlight, the new build appears under your app. Add it to an internal or external test group.
- External groups require beta review (typically a day on first submission, faster after).
- Once tested, promote to the App Store: create a new version (or attach to the in-progress one), select the build, fill in metadata, submit for review.
Android — Google Play workflow¶
eas submit --profile production --platform androiduploads the AAB to Play Console.- The release goes to the internal testing track by default. Pass
--track productionor use Play Console to promote. - Set the rollout percentage in Play Console for a staged rollout. Halt the rollout if early metrics look bad.
OTA updates (EAS Update)¶
For JS-only changes (no native code / dependency changes), you can ship an OTA update without going through the stores:
# Publish to the "production" channel
eas update --branch production --message "Fix calendar timezone bug"
Clients on a build subscribed to that channel pick up the update on their next launch. To revert:
eas update:rollback --branch production
OTA updates are bounded by Apple/Google rules — anything that materially changes app behavior or adds features outside the reviewed scope should go through a full native release.
Beta channels¶
The preview build profile (internal distribution) is the standard channel for beta testing — pre-release builds you want to put in front of a small group before pushing to TestFlight / Play. Distribute via the EAS internal-distribution page; testers install the build with a one-click link on iOS (UDID-provisioned) or APK on Android.
See also¶
- Play Store — Android signing, SHA-1 fingerprints, keystore management
- Native Testing — simulator / device setup for QA
frontend/eas.json— the authoritative build/submit configuration