CI/CD & Automation
Enterprise mobile engineering requires automated Continuous Integration and Continuous Deployment (CI/CD) pipelines (using GitHub Actions, Fastlane, or Codemagic) to enforce code formatting, static analysis, unit testing, automated code signing, and multi-track deployment to TestFlight and Google Play.
1. End-to-End Flutter CI/CD Pipeline Flow
βββββββββββββββββ 1. Git Push / PR ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Developer Workβ βββββββββββββββββββββββββΊ β CI RUNNER PIPELINE β
βββββββββββββββββ β β
β Phase 1: Lint & Format (`flutter analyze`) β
β Phase 2: Unit/Widget Tests (`flutter test`) β
β Phase 3: Code Signing (Fastlane Match / Keystore) β
β Phase 4: Artifact Build (`.aab` / `.ipa`) β
βββββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββ
β
β 2. Automated Release Deploy
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β DISTRIBUTION CHANNELS β
β - iOS TestFlight / App Store Connect β
β - Android Google Play Console (Internal/Production) β
β - Firebase App Distribution β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ2. GitHub Actions Workflow Configuration
name: Flutter CI/CD Pipeline
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: subosito/flutter-action@v2
with:
channel: 'stable'
- name: Install Dependencies
run: flutter pub get
- name: Check Code Formatting
run: dart format --set-exit-if-changed .
- name: Run Static Analyzer
run: flutter analyze
- name: Run Unit & Widget Tests
run: flutter test --coverage
deploy_android:
needs: validate
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: subosito/flutter-action@v2
with:
channel: 'stable'
- name: Decode Android Keystore
run: echo "${{ secrets.ANDROID_KEYSTORE_BASE64 }}" | base64 --decode > android/app/keystore.jks
- name: Build Android App Bundle (AAB)
run: flutter build appbundle --release
env:
KEYSTORE_PATH: keystore.jks
KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
- name: Deploy to Google Play Internal Track via Fastlane
run: cd android && bundle exec fastlane deploy_internal3. Automated Code Signing Mechanics
1. iOS Code Signing: Fastlane Match
Managing iOS certificates and provisioning profiles across multiple developers and headless CI runners is notoriously complex.
Fastlane Match solves this by storing encrypted iOS certificates and provisioning profiles in a private, dedicated Git repository.
- CI Execution: Fastlane decrypts the certificates using a passphrase (
MATCH_PASSWORD) and installs them onto the macOS CI runner automatically (fastlane match appstore --readonly).
2. Android Code Signing: Base64 Keystore Ingestion
Store the release Java Keystore (.jks file) as a Base64-encoded environment secret (ANDROID_KEYSTORE_BASE64) in GitHub Secrets or Codemagic.
- CI Execution: The pipeline decodes the Base64 string back into
keystore.jksat runtime and injects keystore credentials via environment variables duringflutter build appbundle.
4. Multi-Track Release Deployment via Fastlane
# android/fastlane/Fastfile
default_platform(:android)
platform :android do
desc "Submit Android App Bundle to Google Play Internal Track"
lane :deploy_internal do
upload_to_play_store(
track: 'internal',
aab: '../build/app/outputs/bundle/release/app-release.aab',
json_key_data: ENV['GCP_PLAY_STORE_JSON_KEY']
)
end
end5. Trade-offs & Production Considerations
- macOS Runner Costs: macOS CI runners (required for iOS builds) are 10x more expensive per minute than Linux runners on GitHub Actions. Run linting, static analysis, and unit tests on cheap Linux runners (
ubuntu-latest), invoking macOS runners (macos-latest) ONLY for final iOS compilation and code signing. - Secrets Hygiene: Never commit plain text
.jkskeystores,.p12certificates, or Google Play JSON API keys to Git repositories. Always inject credentials through CI secret environment variables.