AquaX Mobile Build And Release
Owner: Mobile, DevOps
Reviewers: QA, Backend, Security, Product, Release Owner
Status: Draft - Current-Code Baseline
Version: 0.1
Last Updated: 2026-09-17
Review Cycle: Per mobile release, signing, store or Expo SDK change
1. Purpose And Scope
This document describes the current AquaX mobile build and release process as implemented in mobile/.
It covers:
- local mobile validation before release;
- Expo prebuild behavior;
- Android AAB build;
- Android Google Play upload;
- iOS App Store Connect build/upload;
- version and build-number handling;
- signing credentials and secrets;
- release checks, rollback and known gaps.
This document is based on current repository files, not on a planned EAS Build workflow. AquaX mobile currently has an Expo project ID in app.json, but no eas.json; release scripts use local expo prebuild, Gradle, Xcode and Fastlane/altool flows.
2. Source Files Reviewed
| File | Purpose |
|---|---|
mobile/package.json |
Expo scripts, dependencies and validation scripts. |
mobile/app.json |
App identity, package IDs, version, plugins and update settings. |
mobile/.env.example |
Public runtime environment variables. |
mobile/secrets/build_config.env.example |
Build credential variable template. |
mobile/scripts/build_android_aab.sh |
Android AAB build script. |
mobile/scripts/deploy_android_playstore.sh |
Android AAB build and Google Play upload script. |
mobile/scripts/build_ios_appstore.sh |
iOS IPA build and App Store Connect upload script. |
mobile/scripts/README_BUILD_IOS_APPSTORE.md |
Existing iOS build guide. |
mobile/scripts/README_DEPLOY_ANDROID_PLAYSTORE.md |
Existing Android Play Store guide. |
docs/Mobile_Architecture.md |
Mobile architecture baseline. |
docs/09_Deployment Plan.md |
Deployment baseline and mobile release status. |
.gitignore, mobile/.gitignore |
Secret/native-output ignore rules. |
3. Current Mobile App Profile
| Item | Current value |
|---|---|
| Framework | Expo React Native |
| Expo SDK | ~56.0.8 |
| React Native | 0.85.3 |
| React | 19.2.3 |
| TypeScript | ~6.0.3 |
| App name | AquaX |
| Expo slug | aquax-mobile |
| App version | 1.0.0 |
| Scheme | aquax |
| Orientation | portrait |
| UI style | light |
| Expo updates | disabled |
| Android package | com.aquax.app |
iOS bundle id in app.json |
com.aquax.app |
| iOS bundle id in iOS build script | vn.aquax.app |
| EAS project id | present in app.json |
| EAS build config | not present |
Important gap:
mobile/app.jsonuses iOS bundle idcom.aquax.app.mobile/scripts/build_ios_appstore.shusesBUNDLE_ID="vn.aquax.app".- Before the next iOS release, the team must choose one official iOS bundle identifier and align
app.json, App Store Connect, provisioning profile and script config.
4. Runtime Environment
The mobile app reads public Expo environment variables:
| Variable | Purpose | Example / Default |
|---|---|---|
EXPO_PUBLIC_API_URL |
Backend API base URL | http://192.168.2.62:3001/api in .env.example |
EXPO_PUBLIC_APP_ENV |
Environment label | development |
Release rule:
- Build artifacts must point to the intended backend API.
- Internal/test builds may use dev or staging APIs.
- Production store builds must use the production API URL.
- Do not commit real secrets or private credentials into
.envormobile/secrets/.
5. Local Validation Before Build
Run validation from mobile/ before creating a store artifact:
cd mobile
npm install
npm run lint
npm run typecheck
Optional local smoke checks:
npm start
npm run android
npm run ios
Current package scripts:
| Script | Purpose |
|---|---|
npm start |
Start Expo development server. |
npm run android |
Run Android target through Expo. |
npm run ios |
Run iOS target through Expo. |
npm run web |
Start Expo web target. |
npm run lint |
Run Expo ESLint. |
npm run typecheck |
Run TypeScript without emitting files. |
npm run format |
Format with Prettier. |
npm run format:check |
Check formatting with Prettier. |
6. Build Number And Versioning
Current app version is read from mobile/app.json:
{
"expo": {
"version": "1.0.0"
}
}
Build scripts use BUILD_NUMBER when provided; otherwise they generate a timestamp-like number.
| Platform | Store field | Script behavior |
|---|---|---|
| Android | android.versionCode |
Generated from BUILD_NUMBER or trimmed timestamp, then written into app.json and Gradle. |
| iOS | ios.buildNumber / CFBundleVersion |
Generated from BUILD_NUMBER or full timestamp, then written into app.json and Info.plist. |
Release rules:
- Increase
expo.versionwhen the user-visible app version changes. - Ensure Android
versionCodeis greater than every previous Play Store upload. - Ensure iOS build number is greater than every previous App Store Connect upload for the same version.
- Record app version, build number, commit SHA, target API URL and store track in release notes.
Known side effect:
- The scripts mutate
mobile/app.jsonduring build by settingandroid.versionCodeorios.buildNumber. - After a local release build, review
git diff mobile/app.jsonand keep or revert the generated build-number change according to the release policy.
7. Secrets And Signing Material
Credential template:
mobile/secrets/build_config.env.example
Real credential location:
mobile/secrets/
This folder is gitignored, except:
mobile/secrets/.gitkeepmobile/secrets/build_config.env.example
Do not add credential values to documentation, tickets, PR comments or logs.
7.1 Android Signing Variables
| Variable | Purpose | Required |
|---|---|---|
ANDROID_KEYSTORE_PATH |
Path to .jks or .keystore; auto-detected from mobile/secrets/ when omitted. |
Required for Play Store upload |
ANDROID_KEYSTORE_PASSWORD |
Keystore password. | Required |
ANDROID_KEY_ALIAS |
Key alias, defaults to upload. |
Optional |
ANDROID_KEY_PASSWORD |
Key password, defaults to keystore password. | Optional |
GOOGLE_PLAY_JSON_KEY |
Google Play service account JSON key. | Required for upload |
BUILD_NUMBER |
Store build number override. | Optional |
7.2 iOS Signing Variables
| Variable | Purpose | Required |
|---|---|---|
CERT_P12_PATH |
Apple Distribution .p12; auto-detected from mobile/secrets/ when possible. |
Required |
CERT_P12_PASSWORD |
.p12 password; can be empty if certificate has no password. |
Optional |
MOBILEPROVISION_PATH |
App Store .mobileprovision; auto-detected from mobile/secrets/ when possible. |
Required |
ASC_KEY_ID |
App Store Connect API key ID; auto-detected from AuthKey_*.p8 filename when possible. |
Required |
ASC_ISSUER_ID |
App Store Connect issuer ID. | Required |
ASC_KEY_PATH |
App Store Connect .p8 path; defaults to AuthKey_${ASC_KEY_ID}.p8. |
Required |
ASC_PROVIDER_PUBLIC_ID |
Provider ID for accounts with multiple teams. | Optional |
BUILD_NUMBER |
Store build number override. | Optional |
8. Android AAB Build
Script:
mobile/scripts/build_android_aab.sh
Purpose:
- generate native Android project with Expo prebuild;
- set Android
versionCode; - apply signing configuration when a keystore is available;
- build a release AAB through Gradle.
Run from repository root:
chmod +x mobile/scripts/build_android_aab.sh
mobile/scripts/build_android_aab.sh
Main steps:
- Load
mobile/secrets/build_config.envwhen present. - Auto-detect Android keystore from
mobile/secrets/. - Generate
BUILD_NUMBERwhen not provided. - Run
npm install --no-audit --no-fund. - Run
npx expo prebuild --platform android --clean. - Write
android.versionCodeintomobile/app.json. - Patch Gradle
versionCodewhen generated native Gradle file exists. - Run
./gradlew bundleRelease. - Print the generated
.aabpath.
Expected artifact:
mobile/android/app/build/outputs/bundle/release/*.aab
9. Android Google Play Release
Script:
mobile/scripts/deploy_android_playstore.sh
Purpose:
- build signed Android AAB;
- upload the AAB to Google Play through Fastlane
supply.
Supported tracks:
| Track | Purpose |
|---|---|
internal |
Internal testing; default and safest first upload target. |
alpha |
Closed testing. |
beta |
Open testing. |
production |
Production release. |
Run from repository root:
chmod +x mobile/scripts/deploy_android_playstore.sh
mobile/scripts/deploy_android_playstore.sh internal
Production upload:
mobile/scripts/deploy_android_playstore.sh production
Main steps:
- Validate the requested track.
- Load
mobile/secrets/build_config.env. - Auto-detect keystore and Google Play JSON key where possible.
- Install or verify Fastlane.
- Run
npx expo prebuild --platform android --clean. - Set Android
versionCode. - Build release AAB with Gradle.
- Upload AAB with
fastlane supply.
Release recommendation:
- Upload to
internalfirst. - Complete smoke testing on installed app.
- Promote to wider tracks or production only after QA/release approval.
10. iOS App Store Connect Release
Script:
mobile/scripts/build_ios_appstore.sh
Purpose:
- generate native iOS project with Expo prebuild;
- configure manual signing;
- build an App Store IPA;
- upload IPA to App Store Connect.
Required build machine:
- macOS;
- Xcode installed and configured;
- Node.js and npm;
- CocoaPods;
- valid Apple Distribution certificate;
- valid App Store provisioning profile;
- valid App Store Connect API key.
Run from repository root:
chmod +x mobile/scripts/build_ios_appstore.sh
mobile/scripts/build_ios_appstore.sh
Main steps:
- Load build variables from
mobile/secrets/build_config.envand selected values frommobile/.env. - Auto-detect
.p12,.mobileprovisionandAuthKey_*.p8where possible. - Create a temporary keychain.
- Import Apple Distribution certificate.
- Install provisioning profile.
- Run
npx expo prebuild --platform ios --clean. - Generate
ExportOptions.plist. - Patch Xcode project for manual signing.
- Run
pod install --repo-update. - Set iOS build number.
- Build archive and export IPA through
xcodebuild. - Upload IPA through
xcrun altool. - Delete temporary keychain on exit.
Expected artifact:
mobile/build/ios/ipa/*.ipa
iOS release blocker:
- Align bundle identifier before App Store release. Current app config and build script do not use the same bundle ID.
11. Release Flow
Recommended release flow:
feature/fix branches
-> mobile validation
-> release branch or approved release commit
-> Android internal / iOS TestFlight candidate
-> QA smoke + regression
-> store production rollout
-> post-release verification
Minimum release checklist:
- PR merged according to
docs/Git_Workflow.md. npm run lintpassed.npm run typecheckpassed.- Backend API target confirmed.
- Store app version and build number confirmed.
- Android/iOS signing credentials confirmed.
- Required environment variables configured.
- Release notes prepared.
- QA smoke test completed on a real device.
- Rollback/recovery plan recorded.
12. QA Smoke Test
Smoke test each store candidate against the intended API:
| Area | Check |
|---|---|
| App launch | Splash screen, icon and first screen render correctly. |
| Environment | App points to intended API base URL. |
| Auth | Login, token refresh and logout work. |
| Role navigation | ADMIN, OWNER, MANAGER and TECHNICIAN routes load according to available accounts. |
| Owner flows | Farm, pond, activity, feeding and ticket/problem surfaces load. |
| Technician flows | Incident list, detail and accept/processing flows load. |
| Notifications | Push registration does not crash and notification list loads. |
| Secure storage | Session restoration works after app restart. |
| Error handling | API/network error states are usable and do not expose secrets. |
13. Rollback And Recovery
Android:
- Prefer staged rollout for production.
- Stop rollout or roll back by promoting a previous release in Google Play Console when available.
- If rollback is not available, submit a higher
versionCodehotfix build.
iOS:
- Use phased release when appropriate.
- Stop phased release in App Store Connect if issues appear.
- Submit a new hotfix build with a higher build number when needed.
Application data:
- Mobile rollback does not roll back backend data or database migrations.
- When mobile changes depend on backend changes, ensure the backend remains backward compatible through the mobile store review and rollout window.
14. Current Automation Status
| Area | Current status |
|---|---|
| Mobile CI in GitHub Actions | Not present in the main CI workflow. |
| Local validation scripts | Present: lint, typecheck, format. |
| Android build script | Present. |
| Android Play Store upload | Present through Fastlane. |
| iOS App Store upload | Present through local macOS/Xcode script. |
| EAS Build | Not configured. |
| Expo OTA updates | Disabled. |
| Store release manifest | Not yet standardized. |
15. Known Gaps
| Gap | Impact | Recommended Follow-up |
|---|---|---|
iOS bundle ID mismatch between app.json and build_ios_appstore.sh |
iOS release/signing can fail or target the wrong app record | Align bundle ID across app config, script, provisioning profile and App Store Connect. |
No eas.json despite EAS project ID |
Release process relies on local machines and custom scripts | Decide whether to keep script-based releases or migrate to EAS Build/Submit. |
Mobile CI is not in .github/workflows/ci.yml |
Mobile regressions may not block PRs | Add mobile install, lint and typecheck jobs. |
Scripts mutate app.json during build |
Build-number changes can be accidentally committed or lost | Define whether build numbers are committed, generated only, or controlled by release tooling. |
iOS script uses altool |
Apple tooling may require future migration | Evaluate notarytool, Transporter or EAS Submit when updating release tooling. |
| Release manifest is not standardized | Store release traceability may vary | Add a mobile release record template covering commit, version, build, store track, API target and QA evidence. |
| Production API URL policy is not formalized for mobile | Store build may point to the wrong API | Add environment-specific build config and verification step. |
16. Quick Commands
Validate mobile:
cd mobile
npm install
npm run lint
npm run typecheck
Build Android AAB:
mobile/scripts/build_android_aab.sh
Upload Android internal test:
mobile/scripts/deploy_android_playstore.sh internal
Upload Android production:
mobile/scripts/deploy_android_playstore.sh production
Build and upload iOS:
mobile/scripts/build_ios_appstore.sh
Set a manual build number:
BUILD_NUMBER=2026091701 mobile/scripts/build_android_aab.sh
BUILD_NUMBER=2026091701 mobile/scripts/build_ios_appstore.sh