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.json uses iOS bundle id com.aquax.app.
  • mobile/scripts/build_ios_appstore.sh uses BUNDLE_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 .env or mobile/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.version when the user-visible app version changes.
  • Ensure Android versionCode is 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.json during build by setting android.versionCode or ios.buildNumber.
  • After a local release build, review git diff mobile/app.json and 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/.gitkeep
  • mobile/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:

  1. Load mobile/secrets/build_config.env when present.
  2. Auto-detect Android keystore from mobile/secrets/.
  3. Generate BUILD_NUMBER when not provided.
  4. Run npm install --no-audit --no-fund.
  5. Run npx expo prebuild --platform android --clean.
  6. Write android.versionCode into mobile/app.json.
  7. Patch Gradle versionCode when generated native Gradle file exists.
  8. Run ./gradlew bundleRelease.
  9. Print the generated .aab path.

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:

  1. Validate the requested track.
  2. Load mobile/secrets/build_config.env.
  3. Auto-detect keystore and Google Play JSON key where possible.
  4. Install or verify Fastlane.
  5. Run npx expo prebuild --platform android --clean.
  6. Set Android versionCode.
  7. Build release AAB with Gradle.
  8. Upload AAB with fastlane supply.

Release recommendation:

  • Upload to internal first.
  • 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:

  1. Load build variables from mobile/secrets/build_config.env and selected values from mobile/.env.
  2. Auto-detect .p12, .mobileprovision and AuthKey_*.p8 where possible.
  3. Create a temporary keychain.
  4. Import Apple Distribution certificate.
  5. Install provisioning profile.
  6. Run npx expo prebuild --platform ios --clean.
  7. Generate ExportOptions.plist.
  8. Patch Xcode project for manual signing.
  9. Run pod install --repo-update.
  10. Set iOS build number.
  11. Build archive and export IPA through xcodebuild.
  12. Upload IPA through xcrun altool.
  13. 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 lint passed.
  • npm run typecheck passed.
  • 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 versionCode hotfix 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