workflows: android-debug: name: Android – Debug APK max_build_duration: 45 instance_type: mac_mini_m2 environment: node: 22 java: 17 cache: cache_paths: - $HOME/.gradle/caches - $HOME/.gradle/wrapper scripts: - name: Install dependencies script: npm install --legacy-peer-deps - name: Build web assets script: npm run build - name: Write Firebase config script: | if [ -n "$GOOGLE_SERVICES_JSON" ]; then echo "$GOOGLE_SERVICES_JSON" | base64 --decode > android/app/google-services.json echo "Wrote google-services.json ($(wc -c < android/app/google-services.json) bytes)" else echo "GOOGLE_SERVICES_JSON is empty — skipping" fi if [ -n "$GOOGLE_SERVICE_INFO_PLIST" ]; then echo "$GOOGLE_SERVICE_INFO_PLIST" | base64 --decode > ios/App/App/GoogleService-Info.plist echo "Wrote GoogleService-Info.plist ($(wc -c < ios/App/App/GoogleService-Info.plist) bytes)" fi - name: Capacitor sync script: npx cap sync android - name: Set up debug keystore script: | if [ -n "$CM_DEBUG_KEYSTORE" ]; then echo "$CM_DEBUG_KEYSTORE" | base64 --decode > /tmp/debug-keystore.p12 fi - name: Build debug APK script: | cd android if [ -n "$CM_DEBUG_KEYSTORE" ]; then ./gradlew assembleDebug \ -Pandroid.injected.signing.store.file=/tmp/debug-keystore.p12 \ -Pandroid.injected.signing.store.password=$CM_DEBUG_KEYSTORE_PASSWORD \ -Pandroid.injected.signing.key.alias=debug \ -Pandroid.injected.signing.key.password=$CM_DEBUG_KEYSTORE_PASSWORD \ --no-daemon else ./gradlew assembleDebug --no-daemon fi artifacts: - android/app/build/outputs/apk/debug/app-debug.apk android-release-apk: name: Android – Release APK max_build_duration: 45 instance_type: mac_mini_m2 environment: node: 22 java: 17 cache: cache_paths: - $HOME/.gradle/caches - $HOME/.gradle/wrapper scripts: - name: Install dependencies script: npm install --legacy-peer-deps - name: Build web assets script: npm run build - name: Write Firebase config script: | if [ -n "$GOOGLE_SERVICES_JSON" ]; then echo "$GOOGLE_SERVICES_JSON" | base64 --decode > android/app/google-services.json fi - name: Capacitor sync script: npx cap sync android - name: Set up keystore script: echo "$CM_KEYSTORE" | base64 --decode > /tmp/keystore.jks - name: Build Release APK script: | cd android ./gradlew assembleRelease \ -Pandroid.injected.signing.store.file=/tmp/keystore.jks \ -Pandroid.injected.signing.store.password=$CM_KEYSTORE_PASSWORD \ -Pandroid.injected.signing.key.alias=$CM_KEY_ALIAS \ -Pandroid.injected.signing.key.password=$CM_KEY_PASSWORD \ --no-daemon artifacts: - android/app/build/outputs/apk/release/app-release.apk android-release-aab: name: Android – Release AAB max_build_duration: 45 instance_type: mac_mini_m2 environment: node: 22 java: 17 cache: cache_paths: - $HOME/.gradle/caches - $HOME/.gradle/wrapper scripts: - name: Install dependencies script: npm install --legacy-peer-deps - name: Build web assets script: npm run build - name: Write Firebase config script: | if [ -n "$GOOGLE_SERVICES_JSON" ]; then echo "$GOOGLE_SERVICES_JSON" | base64 --decode > android/app/google-services.json fi - name: Capacitor sync script: npx cap sync android - name: Set up keystore script: echo "$CM_KEYSTORE" | base64 --decode > /tmp/keystore.jks - name: Build Release AAB script: | cd android ./gradlew bundleRelease \ -Pandroid.injected.signing.store.file=/tmp/keystore.jks \ -Pandroid.injected.signing.store.password=$CM_KEYSTORE_PASSWORD \ -Pandroid.injected.signing.key.alias=$CM_KEY_ALIAS \ -Pandroid.injected.signing.key.password=$CM_KEY_PASSWORD \ --no-daemon artifacts: - android/app/build/outputs/bundle/release/app-release.aab android-release: name: Android – Publish to Google Play max_build_duration: 60 instance_type: mac_mini_m2 environment: node: 22 java: 17 cache: cache_paths: - $HOME/.gradle/caches - $HOME/.gradle/wrapper scripts: - name: Install dependencies script: npm install --legacy-peer-deps - name: Build web assets script: npm run build - name: Write Firebase config script: | if [ -n "$GOOGLE_SERVICES_JSON" ]; then echo "$GOOGLE_SERVICES_JSON" | base64 --decode > android/app/google-services.json fi - name: Capacitor sync script: npx cap sync android - name: Set up keystore script: echo "$CM_KEYSTORE" | base64 --decode > /tmp/keystore.jks - name: Build Release AAB script: | cd android ./gradlew bundleRelease \ -Pandroid.injected.signing.store.file=/tmp/keystore.jks \ -Pandroid.injected.signing.store.password=$CM_KEYSTORE_PASSWORD \ -Pandroid.injected.signing.key.alias=$CM_KEY_ALIAS \ -Pandroid.injected.signing.key.password=$CM_KEY_PASSWORD \ --no-daemon publishing: google_play: credentials: $GOOGLE_PLAY_SERVICE_ACCOUNT_CREDENTIALS track: $GOOGLE_PLAY_TRACK submit_as_draft: false artifacts: - android/app/build/outputs/bundle/release/app-release.aab ios-debug: name: iOS – Debug IPA max_build_duration: 60 instance_type: mac_mini_m2 environment: node: 22 xcode: latest scripts: - name: Install dependencies script: npm install --legacy-peer-deps - name: Build web assets script: npm run build - name: Write Firebase config script: | if [ -n "$GOOGLE_SERVICE_INFO_PLIST" ]; then echo "$GOOGLE_SERVICE_INFO_PLIST" | base64 --decode > ios/App/App/GoogleService-Info.plist ruby -e " require 'xcodeproj' project = Xcodeproj::Project.open('ios/App/App.xcodeproj') target = project.targets.find { |t| t.name == 'App' } app_group = project.main_group['App'] unless app_group.files.find { |f| f.path == 'GoogleService-Info.plist' } file_ref = app_group.new_reference('GoogleService-Info.plist') target.resources_build_phase.add_file_reference(file_ref) project.save end " fi - name: Capacitor sync script: npx cap sync ios - name: Set up signing script: | keychain initialize if [ -n "$APP_STORE_CONNECT_PRIVATE_KEY" ]; then app-store-connect fetch-signing-files \ $(xcode-project detect-bundle-id) \ --type IOS_APP_DEVELOPMENT \ --certificate-key=@env:APP_STORE_CONNECT_PRIVATE_KEY \ --issuer-id=$APP_STORE_CONNECT_ISSUER_ID \ --key-id=$APP_STORE_CONNECT_KEY_IDENTIFIER \ --create xcode-project use-profiles elif [ -n "$CM_CERTIFICATE" ]; then echo "$CM_CERTIFICATE" | base64 --decode > /tmp/cert.p12 keychain add-certificates \ --certificate /tmp/cert.p12 \ --certificate-password "$CM_CERTIFICATE_PASSWORD" mkdir -p "$HOME/Library/MobileDevice/Provisioning Profiles" echo "$CM_PROVISIONING_PROFILE" | base64 --decode > "$HOME/Library/MobileDevice/Provisioning Profiles/profile.mobileprovision" xcode-project use-profiles else echo "No signing credentials configured — build will fail at signing" exit 1 fi - name: Build IPA script: | xcode-project build-ipa \ --project ios/App/App.xcodeproj \ --scheme App artifacts: - build/ios/ipa/*.ipa ios-release: name: iOS – Release IPA max_build_duration: 90 instance_type: mac_mini_m2 environment: node: 22 xcode: latest scripts: - name: Install dependencies script: npm install --legacy-peer-deps - name: Build web assets script: npm run build - name: Write Firebase config script: | if [ -n "$GOOGLE_SERVICE_INFO_PLIST" ]; then echo "$GOOGLE_SERVICE_INFO_PLIST" | base64 --decode > ios/App/App/GoogleService-Info.plist ruby -e " require 'xcodeproj' project = Xcodeproj::Project.open('ios/App/App.xcodeproj') target = project.targets.find { |t| t.name == 'App' } app_group = project.main_group['App'] unless app_group.files.find { |f| f.path == 'GoogleService-Info.plist' } file_ref = app_group.new_reference('GoogleService-Info.plist') target.resources_build_phase.add_file_reference(file_ref) project.save end " fi - name: Capacitor sync script: npx cap sync ios - name: Set up signing script: | keychain initialize if [ -n "$APP_STORE_CONNECT_PRIVATE_KEY" ]; then app-store-connect fetch-signing-files \ $(xcode-project detect-bundle-id) \ --type IOS_APP_STORE \ --certificate-key=@env:APP_STORE_CONNECT_PRIVATE_KEY \ --issuer-id=$APP_STORE_CONNECT_ISSUER_ID \ --key-id=$APP_STORE_CONNECT_KEY_IDENTIFIER \ --create xcode-project use-profiles else echo "$CM_CERTIFICATE" | base64 --decode > /tmp/cert.p12 keychain add-certificates \ --certificate /tmp/cert.p12 \ --certificate-password "$CM_CERTIFICATE_PASSWORD" mkdir -p "$HOME/Library/MobileDevice/Provisioning Profiles" echo "$CM_PROVISIONING_PROFILE" | base64 --decode > "$HOME/Library/MobileDevice/Provisioning Profiles/profile.mobileprovision" xcode-project use-profiles fi - name: Build IPA script: | xcode-project build-ipa \ --project ios/App/App.xcodeproj \ --scheme App \ --config Release - name: Publish script: | if [ "$SUBMIT_TO_TESTFLIGHT" = "true" ] || [ "$SUBMIT_TO_APP_STORE" = "true" ]; then app-store-connect publish \ --certificate-key=@env:APP_STORE_CONNECT_PRIVATE_KEY \ --issuer-id=$APP_STORE_CONNECT_ISSUER_ID \ --key-id=$APP_STORE_CONNECT_KEY_IDENTIFIER \ --submit-to-testflight=$SUBMIT_TO_TESTFLIGHT \ --submit-to-app-store=$SUBMIT_TO_APP_STORE fi artifacts: - build/ios/ipa/*.ipa