GitHub Actions Workflows
What you'll learn
- Setting up PR validation and build/deploy workflows
- Using affected package detection to skip unchanged packages in CI
- Managing secrets for code signing and store uploads
- Configuring matrix builds for multiple flavors
Prerequisites
- An existing Archipelago monorepo (see Monorepo Scaffolding)
- A GitHub repository with Actions enabled
- Store credentials ready (App Store Connect API key, Play Console service account)
Step 1: Generate the CI Workflows
bash
archipelago generate github_actionsYou will be prompted for:
- appName — Your app name (e.g.,
MyApp) - flavors — Comma-separated list of flavors (e.g.,
dev,staging,production) - enableAffected — Whether to use affected detection (
true)
Step 2: Understand the Generated Workflows
.github/workflows/
├── pr-validation.yml # Runs on every PR — lint, test, affected check
├── build-android.yml # Reusable workflow for Android builds
├── build-ios.yml # Reusable workflow for iOS builds
└── deploy.yml # Deploy to stores on tag pushStep 3: PR Validation Workflow
The pr-validation.yml workflow runs on every pull request to catch issues early:
yaml
name: PR Validation
on:
pull_request:
branches: [develop, main]
jobs:
analyze:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: subosito/flutter-action@v2
- name: Detect affected packages
id: affected
run: dart run devtools affected --base origin/${{ github.base_ref }} --format json > affected.json
- name: Analyze affected packages
run: |
for pkg in $(jq -r '.[]' affected.json); do
echo "Analyzing $pkg..."
cd "$pkg" && flutter analyze && cd -
done
- name: Test affected packages
run: |
for pkg in $(jq -r '.[]' affected.json); do
echo "Testing $pkg..."
cd "$pkg" && flutter test && cd -
doneStep 4: Configure Secrets
Add the following secrets in your GitHub repository settings under Settings > Secrets and variables > Actions:
| Secret | Purpose |
|---|---|
MATCH_GIT_TOKEN | Access to your Match certificates repo |
MATCH_PASSWORD | Encryption password for Match |
APP_STORE_CONNECT_API_KEY | Base64-encoded App Store Connect API key |
PLAY_STORE_SERVICE_ACCOUNT | Base64-encoded Google Play service account JSON |
KEYSTORE_BASE64 | Base64-encoded Android signing keystore |
KEYSTORE_PASSWORD | Android keystore password |
Encode files to base64 for storage as secrets:
bash
base64 -i path/to/AuthKey.p8 | pbcopy
base64 -i path/to/keystore.jks | pbcopyStep 5: Deploy Workflow
The deploy workflow triggers on version tags and builds for the target platform:
yaml
name: Deploy
on:
push:
tags:
- 'v*'
jobs:
deploy-android:
uses: ./.github/workflows/build-android.yml
with:
flavor: production
track: internal
secrets: inherit
deploy-ios:
uses: ./.github/workflows/build-ios.yml
with:
flavor: production
secrets: inheritStep 6: Using Affected Detection in CI
When enableAffected is true, CI only builds and tests packages that changed. This dramatically reduces pipeline time in large monorepos:
yaml
- name: Check if app is affected
id: check
run: |
affected=$(dart run devtools affected --base origin/main --format json)
if echo "$affected" | jq -e '.[] | select(. == "app")' > /dev/null; then
echo "should_build=true" >> $GITHUB_OUTPUT
else
echo "should_build=false" >> $GITHUB_OUTPUT
fi
- name: Build app
if: steps.check.outputs.should_build == 'true'
run: flutter build appbundle --flavor productionKey Customization Points
| Customization | Where to Change |
|---|---|
| Trigger branches | on.pull_request.branches in each workflow |
| Flutter version | subosito/flutter-action version input |
| Affected base branch | --base flag in affected command |
| Deploy track (Android) | track input in deploy workflow |
| Runner type | runs-on — use macos-latest for iOS |
Next Steps
- Configure Fastlane for store deployment lanes
- Set up build_prepare to strip debug packages before release builds