name: Build & Deploy iOS to TestFlight on: push: branches: - main workflow_dispatch: jobs: build-and-deploy: runs-on: macos-latest steps: - name: Checkout Repository uses: actions/checkout@v4 - name: Setup Flutter uses: subosito/flutter-action@v2 with: channel: "stable" cache: true - name: Install Dependencies run: flutter pub get # ── Code Signing Setup ────────────────────────────────────────────────── - name: Install Apple Certificate env: BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }} P12_PASSWORD: ${{ secrets.P12_PASSWORD }} KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }} run: | CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12 echo -n "$BUILD_CERTIFICATE_BASE64" | base64 --decode -o $CERTIFICATE_PATH KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH security set-keychain-settings -lut 21600 $KEYCHAIN_PATH security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH security import $CERTIFICATE_PATH -P "$P12_PASSWORD" -A \ -t cert -f pkcs12 -k $KEYCHAIN_PATH security set-key-partition-list -S apple-tool:,apple: \ -s -k "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH security list-keychain -d user -s $KEYCHAIN_PATH - name: Install Provisioning Profile env: BUILD_PROVISION_PROFILE_BASE64: ${{ secrets.BUILD_PROVISION_PROFILE_BASE64 }} run: | PP_PATH=$RUNNER_TEMP/build_pp.mobileprovision echo -n "$BUILD_PROVISION_PROFILE_BASE64" | base64 --decode -o $PP_PATH mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles # Xcode requires the filename to be the profile's UUID UUID=$(security cms -D -i $PP_PATH | plutil -extract UUID raw -) cp $PP_PATH ~/Library/MobileDevice/Provisioning\ Profiles/$UUID.mobileprovision echo "Installed provisioning profile UUID: $UUID" # ── Disable SPM ───────────────────────────────────────────────────────── - name: Disable Swift Package Manager run: flutter config --no-enable-swift-package-manager # ── CocoaPods ─────────────────────────────────────────────────────────── - name: Install CocoaPods Dependencies run: | cd ios rm -f Podfile.lock pod install --repo-update # ── Build & Archive ───────────────────────────────────────────────────── - name: Build iOS (no codesign) run: flutter build ios --release --no-codesign - name: Re-run pod install to apply Podfile signing settings run: | cd ios pod install - name: Archive with Xcode env: TEAM_ID: ${{ secrets.TEAM_ID }} run: | cat > $RUNNER_TEMP/signing.xcconfig << 'XCCONFIG' CODE_SIGN_STYLE = Manual CODE_SIGN_IDENTITY = iPhone Distribution PROVISIONING_PROFILE_SPECIFIER = Enaklo Owner App Store CODE_SIGNING_REQUIRED = YES XCCONFIG xcodebuild archive \ -workspace ios/Runner.xcworkspace \ -scheme Runner \ -configuration Release \ -archivePath $RUNNER_TEMP/Runner.xcarchive \ -destination "generic/platform=iOS" \ -xcconfig $RUNNER_TEMP/signing.xcconfig \ DEVELOPMENT_TEAM="$TEAM_ID" - name: Export IPA run: | xcodebuild -exportArchive \ -archivePath $RUNNER_TEMP/Runner.xcarchive \ -exportPath $RUNNER_TEMP/export \ -exportOptionsPlist ios/ExportOptions.plist mkdir -p build/ios/ipa cp $RUNNER_TEMP/export/*.ipa build/ios/ipa/ # ── Upload to TestFlight ──────────────────────────────────────────────── - name: Upload to TestFlight via Transporter env: APPLE_ID: ${{ secrets.APPLE_ID }} APP_SPECIFIC_PASSWORD: ${{ secrets.APP_SPECIFIC_PASSWORD }} run: | xcrun altool --upload-app \ --type ios \ --file "build/ios/ipa/*.ipa" \ --username "$APPLE_ID" \ --password "$APP_SPECIFIC_PASSWORD" \ --verbose # ── Cleanup ───────────────────────────────────────────────────────────── - name: Clean Up Keychain and Provisioning Profile if: ${{ always() }} run: | security delete-keychain $RUNNER_TEMP/app-signing.keychain-db || true rm -rf ~/Library/MobileDevice/Provisioning\ Profiles/*.mobileprovision || true # ── Artifact ──────────────────────────────────────────────────────────── - name: Upload IPA as Artifact if: always() uses: actions/upload-artifact@v4 with: name: ios-ipa path: build/ios/ipa/*.ipa retention-days: 7