Mobile deployment
Skill JaveedIshaq/ai-workflow-orchestrator/skills/ballee/mobile-deployment
Reusable Claude Code skills and agents for e-commerce, analytics, development, and project-specific workflowsFrom the repository description
npx -y skills add JaveedIshaq/ai-workflow-orchestrator --skill mobile-deploymentAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
14.7 KB, as published. Nobody here has run it
Mobile Deployment Skill
Comprehensive guide for deploying the Ballee mobile app to iOS (TestFlight/App Store) and Android (Play Store).
Overview
| Platform | CI/CD | Local Deploy | Distribution |
|---|---|---|---|
| iOS | Xcode Cloud | Fastlane / xcodebuild | TestFlight, App Store |
| Android | GitHub Actions | Fastlane / Gradle | Firebase App Distribution, Play Store |
Prerequisites
iOS Prerequisites
- Apple Developer Account with distribution certificate
- App Store Connect API Key at
~/.appstoreconnect/private_keys/AuthKey_LGU934Y2XR.p8 - Xcode installed with command line tools
- Ruby & Bundler for Fastlane
- CocoaPods for iOS dependencies
Key Identifiers:
| Item | Value |
|---|---|
| Bundle ID | co.ballee |
| Team ID | A86CXY8H75 |
| API Key ID | LGU934Y2XR |
| API Issuer ID | 69a6de96-5d75-47e3-e053-5b8c7c11a4d1 |
| Apple ID | [email protected] |
Android Prerequisites
- Release Keystore configured in
apps/mobile/android/key.properties - Google Play Console access with service account
- Java 11+ and Android SDK
- Flutter SDK installed
Key Identifiers:
| Item | Value |
|---|---|
| Package Name | co.ballee |
| Min SDK | 24 |
iOS Deployment
Option 1: Xcode Cloud (Automated - Recommended)
Xcode Cloud automatically builds and deploys to TestFlight on push to main.
Trigger a build manually:
cd apps/mobile/ios/scripts
python3 xcode_cloud_cli.py trigger
Check build status:
python3 xcode_cloud_cli.py list
Environment variables (set in Xcode Cloud):
| Variable | Description |
|---|---|
SUPABASE_URL | Production Supabase URL |
SUPABASE_ANON_KEY | Production anon key |
SENTRY_DSN | Sentry DSN for crash reporting |
MIXPANEL_TOKEN | Analytics token |
APP_STORE_CONNECT_API_KEY_ID | API key ID |
APP_STORE_CONNECT_API_ISSUER_ID | Issuer ID |
APP_STORE_CONNECT_API_KEY_CONTENT | Base64-encoded .p8 key |
CI Scripts:
ci_post_clone.sh- Installs Flutter, builds iOS app, runs code generationci_post_xcodebuild.sh- Uploads to TestFlight, uploads dSYMs to Sentry
Option 2: Fastlane (Local)
Setup (first time):
cd apps/mobile/ios
bundle install
Deploy to TestFlight:
cd apps/mobile/ios
# Option A: Manual signing (uses match provisioning profiles)
bundle exec fastlane beta
# Option B: Automatic signing (uses Xcode managed signing)
bundle exec fastlane beta_auto
# Option C: Build only (no upload)
bundle exec fastlane build_only
Available Lanes:
| Lane | Description |
|---|---|
beta | Build with manual signing + upload to TestFlight |
beta_auto | Build with automatic signing + upload to TestFlight |
build_only | Build IPA without uploading |
Increment build number:
bundle exec fastlane beta increment_build:true
Option 3: Manual xcodebuild
Step 1: Build Flutter app
cd apps/mobile
flutter pub get
dart run build_runner build --delete-conflicting-outputs
flutter build ios --release --no-codesign \
--dart-define=ENV=prod \
--dart-define=BACKEND_URL="https://csjruhqyqzzqxnfeyiaf.supabase.co" \
--dart-define=SUPABASE_TOKEN="$SUPABASE_ANON_KEY"
Step 2: Install pods
cd ios
pod install --repo-update
Step 3: Archive
xcodebuild -workspace Ballee.xcworkspace \
-scheme Ballee \
-sdk iphoneos \
-configuration Release \
-archivePath build/Ballee.xcarchive \
archive
Step 4: Export and upload
xcodebuild -exportArchive \
-archivePath build/Ballee.xcarchive \
-exportOptionsPlist ExportOptions.plist \
-exportPath build/export \
-allowProvisioningUpdates \
-authenticationKeyPath ~/.appstoreconnect/private_keys/AuthKey_LGU934Y2XR.p8 \
-authenticationKeyID LGU934Y2XR \
-authenticationKeyIssuerID 69a6de96-5d75-47e3-e053-5b8c7c11a4d1
iOS Quick Deploy Script
Use the provided script for one-command deployment:
./.claude/skills/mobile-deployment/scripts/deploy-ios.sh
Options:
--increment- Increment build number--skip-upload- Build only, don't upload
Android Deployment
Setting Up Release Signing
Step 1: Create keystore (if needed)
./.claude/skills/mobile-deployment/scripts/setup-android-signing.sh
Or manually:
keytool -genkey -v \
-keystore ~/ballee-release-key.jks \
-keyalg RSA \
-keysize 2048 \
-validity 10000 \
-alias ballee
Step 2: Create key.properties
Create apps/mobile/android/key.properties:
storePassword=<keystore password>
keyPassword=<key password>
keyAlias=ballee
storeFile=/path/to/ballee-release-key.jks
Step 3: Enable release signing
Uncomment the signing config in apps/mobile/android/app/build.gradle.kts:
signingConfigs {
create("release") {
keyAlias = keystoreProperties["keyAlias"] as String
keyPassword = keystoreProperties["keyPassword"] as String
storeFile = keystoreProperties["storeFile"]?.let { file(it) }
storePassword = keystoreProperties["storePassword"] as String
}
}
buildTypes {
release {
signingConfig = signingConfigs.getByName("release")
}
}
Building Release APK/AAB
Build AAB (for Play Store):
cd apps/mobile
flutter build appbundle --release \
--dart-define=ENV=prod \
--dart-define=BACKEND_URL="https://csjruhqyqzzqxnfeyiaf.supabase.co" \
--dart-define=SUPABASE_TOKEN="$SUPABASE_ANON_KEY"
Output: build/app/outputs/bundle/release/app-release.aab
Build APK (for direct install/testing):
flutter build apk --release \
--dart-define=ENV=prod \
--dart-define=BACKEND_URL="https://csjruhqyqzzqxnfeyiaf.supabase.co" \
--dart-define=SUPABASE_TOKEN="$SUPABASE_ANON_KEY"
Output: build/app/outputs/flutter-apk/app-release.apk
Fastlane for Android
Setup (first time):
cd apps/mobile/android
bundle install
Available Lanes:
| Lane | Description |
|---|---|
build_apk | Build release APK |
build_aab | Build release AAB |
firebase | Build and deploy to Firebase App Distribution |
playstore | Build and upload to Play Store (internal track) |
Deploy to Firebase App Distribution:
bundle exec fastlane firebase
Deploy to Play Store:
bundle exec fastlane playstore
Manual Play Store Upload
- Go to Google Play Console
- Select "Ballee" app
- Go to Release > Production (or Testing tracks)
- Create new release
- Upload the AAB file from
build/app/outputs/bundle/release/app-release.aab - Add release notes
- Review and roll out
Android Quick Deploy Script
Use the provided script for one-command deployment:
./.claude/skills/mobile-deployment/scripts/deploy-android.sh
Options:
--apk- Build APK instead of AAB--firebase- Deploy to Firebase App Distribution--playstore- Deploy to Play Store
Android CI/CD with GitHub Actions
Overview
Android deployments are fully automated via GitHub Actions with:
- Workload Identity Federation for keyless Google Cloud authentication
- Automatic builds on push to
mainanddev - Firebase App Distribution for internal testing
- Play Store deployment on version tags
- Staged production rollouts (10% → 50% → 100%)
Workflow Triggers
| Trigger | Action |
|---|---|
Push to dev | Build and validate |
Push to main | Build + Deploy to Firebase |
PR to main/dev | Build and validate |
Tag v*.*.* | Build + Deploy to Play Store (internal) |
Tag release/* | Promote to Production (10% rollout) |
| Manual dispatch | Configurable deployment |
Setup Scripts
Run these scripts to set up Android CI/CD:
# 1. Create release keystore and local key.properties
./scripts/setup-android-keystore.sh
# 2. Set up Workload Identity Federation (requires gcloud CLI)
./scripts/setup-gcloud-wif.sh
# 3. Configure GitHub repository secrets
./scripts/setup-android-secrets.sh
Required GitHub Secrets
| Secret | Description | How to Obtain |
|---|---|---|
ANDROID_KEYSTORE_BASE64 | Base64-encoded keystore | openssl base64 < keystore.jks |
ANDROID_KEYSTORE_PASSWORD | Keystore password | Set during keystore creation |
ANDROID_KEY_ALIAS | Key alias | Usually ballee |
ANDROID_KEY_PASSWORD | Key password | Set during keystore creation |
SUPABASE_URL | Production Supabase URL | .env.local |
SUPABASE_ANON_KEY | Production anon key | .env.local |
FIREBASE_APP_ID_ANDROID | Firebase App ID | Firebase Console |
FIREBASE_SERVICE_ACCOUNT | Firebase SA JSON | Firebase Console |
PLAY_STORE_SERVICE_ACCOUNT | Play Store SA JSON | Google Play Console |
Workload Identity Federation (Keyless Auth)
Instead of storing long-lived service account JSON keys, we use OIDC-based keyless authentication:
- No secrets to rotate - Short-lived credentials (1 hour expiry)
- Fine-grained scoping - Restricted to specific repository/branch
- Google's recommended approach - More secure than service account keys
The setup-gcloud-wif.sh script automates the WIF setup:
- Creates Workload Identity Pool
- Creates OIDC Provider for GitHub
- Creates service account with proper IAM bindings
Manual Workflow Trigger
Trigger a deployment manually via GitHub CLI:
# Deploy to Firebase
gh workflow run android-deploy.yml -f deploy_target=firebase
# Deploy to Play Store (internal)
gh workflow run android-deploy.yml -f deploy_target=playstore-internal
# Promote to production
gh workflow run android-deploy.yml -f deploy_target=playstore-production
Staged Rollouts
Production releases use staged rollouts for safety:
# Promote with 10% rollout (default)
fastlane promote_to_production
# Increase to 50%
fastlane increase_rollout rollout:0.5
# Full rollout
fastlane increase_rollout rollout:1.0
CI/CD Files
| File | Purpose |
|---|---|
.github/workflows/android-deploy.yml | Main CI/CD workflow |
scripts/setup-android-keystore.sh | Keystore creation script |
scripts/setup-gcloud-wif.sh | Workload Identity Federation setup |
scripts/setup-android-secrets.sh | GitHub secrets configuration |
First-Time Setup Checklist
- Run
./scripts/setup-android-keystore.shto create keystore - Back up keystore file to 1Password
- Run
./scripts/setup-gcloud-wif.sh(requires gcloud CLI) - Link service account in Google Play Console (Settings → API access)
- Run
./scripts/setup-android-secrets.shto configure GitHub - Do first manual Play Store release (Google requirement)
- Test workflow with manual dispatch
Environment Variables
Production Environment
All environment variables are passed via --dart-define at build time:
| Variable | Description | iOS | Android |
|---|---|---|---|
ENV | Environment (prod, staging, dev) | Yes | Yes |
BACKEND_URL | Supabase URL | Yes | Yes |
SUPABASE_TOKEN | Supabase anon key | Yes | Yes |
SENTRY_DSN | Sentry DSN | Yes | Yes |
MIXPANEL_TOKEN | Mixpanel token | Yes | Yes |
APP_STORE_ID | App Store ID (iOS only) | Yes | No |
Production Values
SUPABASE_URL="https://csjruhqyqzzqxnfeyiaf.supabase.co"
SUPABASE_ANON_KEY="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImNzanJ1aHF5cXp6cXhuZmV5aWFmIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTgzNDkxOTQsImV4cCI6MjA3MzkyNTE5NH0.wyDYf6UpJr2trbGOzWx-Sf0p5pSbnTWcQOCFnvtN5lI"
File Reference
iOS Files
| File | Purpose |
|---|---|
apps/mobile/ios/fastlane/Fastfile | Fastlane lane definitions |
apps/mobile/ios/fastlane/Appfile | App Store Connect config |
apps/mobile/ios/Gemfile | Ruby dependencies |
apps/mobile/ios/ci_scripts/ci_post_clone.sh | Xcode Cloud setup script |
apps/mobile/ios/ci_scripts/ci_post_xcodebuild.sh | Post-build upload script |
apps/mobile/ios/scripts/xcode_cloud_cli.py | CLI for Xcode Cloud API |
apps/mobile/ios/ExportOptions.plist | Export configuration |
apps/mobile/ios/Ballee/Info.plist | App configuration |
Android Files
| File | Purpose |
|---|---|
apps/mobile/android/app/build.gradle.kts | Gradle build config |
apps/mobile/android/key.properties | Signing credentials (not in git) |
apps/mobile/android/fastlane/Fastfile | Fastlane lane definitions |
apps/mobile/android/fastlane/Appfile | Play Store config |
Troubleshooting
iOS Issues
"Preparing build for App Store Connect failed" (Xcode Cloud)
- This is often an Apple infrastructure issue
- Try changing Xcode version in workflow settings
- Fallback: Use local Fastlane deployment
"No signing certificate found" / "Revoked certificate"
- Check Xcode > Settings > Accounts for valid certificates
- Create new distribution certificate in Apple Developer Portal
- For Xcode Cloud: Certificates are managed automatically
"Missing purpose string in Info.plist"
- Add required
NS*UsageDescriptionkeys toInfo.plist - Common ones:
NSPhotoLibraryUsageDescription,NSCameraUsageDescription
Podfile.lock out of sync
cd apps/mobile/ios
rm -rf Pods Podfile.lock
pod install --repo-update
Build number already exists
# Increment via Fastlane
bundle exec fastlane beta increment_build:true
# Or manually in Xcode
# Ballee.xcodeproj > Build Settings > Current Project Version
Android Issues
"Keystore was tampered with, or password was incorrect"
- Verify passwords in
key.propertiesmatch keystore - Regenerate keystore if lost
"No key with alias 'ballee' found in keystore"
- Check
keyAliasinkey.propertiesmatches alias used during keystore creation
Release build not signed
- Ensure
key.propertiesexists and paths are correct - Uncomment signing config in
build.gradle.kts
App crashes on release but not debug
- Check ProGuard/R8 rules
- Verify environment variables are passed correctly
General Flutter Issues
"pub get failed"
flutter clean
flutter pub get
"build_runner failed"
dart run build_runner clean
dart run build_runner build --delete-conflicting-outputs
Related Skills
xcode-cloud-cicd- Detailed Xcode Cloud CI/CD documentationflutter-development- Flutter development patternsflutter-testing- Testing patterns for mobile
Quick Reference
Deploy iOS to TestFlight
cd apps/mobile/ios && bundle exec fastlane beta_auto
Deploy Android to Play Store
cd apps/mobile/android && bundle exec fastlane playstore
Trigger Xcode Cloud Build
cd apps/mobile/ios/scripts && python3 xcode_cloud_cli.py trigger