Flutter: CI/CD自動化
手動リリースはヒューマンエラーの温床 — 自動化がすべてのステップを反復可能、追跡可能、信頼できるものにします。
📋 前提条件: 以下を先にマスターしている必要があります
- レッスン23:WebとDesktopマルチプラットフォームデプロイ
1. このレッスンで学ぶこと
- GitHub Actionsワークフロー:flutter test / flutter build マルチマトリックスビルド
- コード署名自動化:Androidキーストア / iOS証明書とProvisioning Profile管理
- Fastlane連携:自動スクリーンショット / 自動TestFlight / Play Consoleアップロード
- Codemagic / Bitrise クラウドCIソリューション比較
- ShopApp:GitHub Actionsクロスプラットフォーム自動ビルド + Slack通知 + 自動タグリリース
2. 手動リリース災害のリアルなストーリー
(1) 悸み:4時間の手動リリース + 3回の手戻り
Bobが新しいShopAppバージョンをリリースするたびに:手動でflutter build → 署名 → Play Consoleにアップロード → スクリーンショット撮影 → リリースノート記入 → 審査提出。iOSプロセスはさらに複雑:Archive → IPAエクスポート → App Store Connectにアップロード → 審査待ち。プロセス全体で4時間、最低2回の手戻り(バージョン番号更新忘れ / 署名設定ミス / スクリーンショット非準拠)。リリースのたびにBobは深夜まで働いています。
(2) CI/CDパイプラインソリューション
GitHub Actions自動化:タグをpush → 自動テスト → 自動ビルド → 自動署名 → 自動アップロード → Slack通知。全工程の手動介入ゼロ。
YAML
# Push tag v1.2.3 → automatic release pipeline
on: push: tags: ['v*']
# → flutter test → flutter build → sign → upload → notify
(3) 成果:1時間リリース + 手戻りゼロ
CI/CD構築後、Bobのリリース時間は4時間から1時間に短縮(主に審査待ち)、手戻り率は60%から0%に低下。
3. CI/CDパイプライン概要
graph LR
subgraph GitHub Actions Pipeline
P[Push / PR] --> LINT[Lint & Analyze]
LINT --> TEST[flutter test]
TEST --> BUILD[Build APK/IPA/Web]
BUILD --> SIGN[Code Signing]
SIGN --> DIST[Distribute]
DIST --> |Slack| NOTIFY[Team Notification]
end
TEXT
> 出力: ローカルのFlutter SDKで実行してください(Flutter 3.x / Dart 3.x)。PistonサーバーにはFlutterがインストールされていません — 手元のマシンで`flutter run`して動作確認してください。実際のUI/状態はプラットフォームにより多少異なる場合があります。
(1) CI/CDソリューション比較
| ソリューション | 価格 | プラットフォーム対応 | 設定方法 | 最適な用途 |
|---|---|---|---|---|
| GitHub Actions | 無料(2000分/月) | 全プラットフォーム | YAML | 第一選択 |
| Codemagic | 無料(500分/月) | 全プラットフォーム | YAML | macOSビルドに優位 |
| Bitrise | 無料(200分/月) | 全プラットフォーム | UI + YAML | iOS特化 |
| Fastlane | 無料(ローカル) | iOS/Android | Ruby | ローカル自動化 |
| Cirrus CI | 無料(オープンソース) | 全プラットフォーム | YAML | コミュニティプロジェクト |
4. GitHub Actionsワークフロー
▶ サンプル
TEXT
> 出力: ローカルのFlutter SDKで実行してください(Flutter 3.x / Dart 3.x)。PistonサーバーにはFlutterがインストールされていません — 手元のマシンで`flutter run`して動作確認してください。実際のUI/状態はプラットフォームにより多少異なる場合があります。
:PRチェックワークフロー
YAML
# .github/workflows/pr-check.yml
name: PR Check
on:
pull_request:
branches: [main]
jobs:
analyze-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: '3.x'
channel: 'stable'
- name: Install dependencies
run: flutter pub get
- name: Analyze code
run: flutter analyze --no-fatal-infos
- name: Run unit and widget tests
run: flutter test --coverage
- name: Upload coverage
uses: codecov/codecov-action@v3
with:
files: coverage/lcov.info
▶ サンプル
TEXT
> 出力: ローカルのFlutter SDKで実行してください(Flutter 3.x / Dart 3.x)。PistonサーバーにはFlutterがインストールされていません — 手元のマシンで`flutter run`して動作確認してください。実際のUI/状態はプラットフォームにより多少異なる場合があります。
:マルチマトリックスビルドワークフロー
YAML
# .github/workflows/build.yml
name: Build All Platforms
on:
push:
tags: ['v*']
jobs:
build-android:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: subosito/flutter-action@v2
with: { flutter-version: '3.x' }
- run: flutter pub get
- run: flutter test
- name: Build APK
run: flutter build apk --release
- name: Build AAB
run: flutter build appbundle --release
env:
KEYSTORE_BASE64: ${{ secrets.KEYSTORE_BASE64 }}
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
- name: Upload to Play Console
uses: r0adkll/upload-google-play@v1
with:
serviceAccountJsonPlainText: ${{ secrets.PLAY_SERVICE_ACCOUNT }}
packageName: com.shopapp.app
releaseFiles: build/app/outputs/bundle/release/app-release.aab
track: production
build-ios:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- uses: subosito/flutter-action@v2
with: { flutter-version: '3.x' }
- run: flutter pub get
- name: Install CocoaPods
run: cd ios && pod install
- name: Build IPA
run: flutter build ipa --release --export-options-plist=ios/ExportOptions.plist
- name: Upload to App Store Connect
uses: apple/upload-testflight@v1
with:
app-path: build/ios/ipa/shopapp.ipa
issuer-id: ${{ secrets.APPSTORE_ISSUER_ID }}
api-key-id: ${{ secrets.APPSTORE_KEY_ID }}
api-private-key: ${{ secrets.APPSTORE_PRIVATE_KEY }}
build-web:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: subosito/flutter-action@v2
with: { flutter-version: '3.x' }
- run: flutter pub get
- run: flutter build web --wasm
- name: Deploy to Firebase Hosting
uses: FirebaseExtended/action-hosting-deploy@v0
with:
repoToken: ${{ secrets.GITHUB_TOKEN }}
firebaseServiceAccount: ${{ secrets.FIREBASE_SERVICE_ACCOUNT }}
channelId: live
notify:
needs: [build-android, build-ios, build-web]
runs-on: ubuntu-latest
steps:
- name: Slack Notification
uses: slackapi/slack-github-action@v1
with:
payload: |
{"text": "ShopApp ${{ github.ref_name }} released successfully!"}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}
5. コード署名自動化
▶ サンプル
TEXT
> 出力: ローカルのFlutter SDKで実行してください(Flutter 3.x / Dart 3.x)。PistonサーバーにはFlutterがインストールされていません — 手元のマシンで`flutter run`して動作確認してください。実際のUI/状態はプラットフォームにより多少異なる場合があります。
:Android署名設定
YAML
# GitHub Secrets needed:
# KEYSTORE_BASE64: base64 encoded .jks file
# KEY_PASSWORD: keystore password
# KEY_ALIAS: key alias
- name: Decode keystore
run: echo "$KEYSTORE_BASE64" | base64 --decode > android/app/keystore.jks
env:
KEYSTORE_BASE64: ${{ secrets.KEYSTORE_BASE64 }}
GROOVY
// android/app/build.gradle
android {
signingConfigs {
release {
storeFile file('keystore.jks')
storePassword System.getenv("KEY_PASSWORD")
keyAlias System.getenv("KEY_ALIAS")
keyPassword System.getenv("KEY_PASSWORD")
}
}
buildTypes {
release {
signingConfig signingConfigs.release
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
TEXT
> 出力: ローカルのFlutter SDKで実行してください(Flutter 3.x / Dart 3.x)。PistonサーバーにはFlutterがインストールされていません — 手元のマシンで`flutter run`して動作確認してください。実際のUI/状態はプラットフォームにより多少異なる場合があります。
6. Fastlane連携
▶ サンプル
TEXT
> 出力: ローカルのFlutter SDKで実行してください(Flutter 3.x / Dart 3.x)。PistonサーバーにはFlutterがインストールされていません — 手元のマシンで`flutter run`して動作確認してください。実際のUI/状態はプラットフォームにより多少異なる場合があります。
:Fastlane設定
RUBY
# ios/fastlane/Fastfile
default_platform(:ios)
platform :ios do
desc "Build and upload to TestFlight"
lane :beta do
build_app(
workspace: "Runner.xcworkspace",
scheme: "Runner",
export_method: "app-store",
)
upload_to_testflight(
skip_waiting_for_build_processing: true,
)
end
desc "Take screenshots"
lane :screenshots do
capture_screenshots(
scheme: "Runner",
devices: ["iPhone 15 Pro", "iPad Pro (12.9-inch)"],
)
end
end
TEXT
> 出力: ローカルのFlutter SDKで実行してください(Flutter 3.x / Dart 3.x)。PistonサーバーにはFlutterがインストールされていません — 手元のマシンで`flutter run`して動作確認してください。実際のUI/状態はプラットフォームにより多少異なる場合があります。
RUBY
# android/fastlane/Fastfile
default_platform(:android)
platform :android do
desc "Build and upload to Play Console"
lane :beta do
gradle(task: "bundleRelease")
upload_to_play_store(
track: "internal",
aab: "../build/app/outputs/bundle/release/app-release.aab",
)
end
end
TEXT
> 出力: ローカルのFlutter SDKで実行してください(Flutter 3.x / Dart 3.x)。PistonサーバーにはFlutterがインストールされていません — 手元のマシンで`flutter run`して動作確認してください。実際のUI/状態はプラットフォームにより多少異なる場合があります。
7. 完全な例:ShopApp CI/CD設定
YAML
# .github/workflows/release.yml
name: Release ShopApp
on:
push:
tags: ['v*.*.*']
concurrency:
group: release-${{ github.ref }}
cancel-in-progress: true
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: subosito/flutter-action@v2
with: { flutter-version: '3.x', channel: 'stable' }
- run: flutter pub get
- run: flutter analyze --no-fatal-infos
- run: flutter test --coverage
- uses: codecov/codecov-action@v3
build-android:
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: subosito/flutter-action@v2
with: { flutter-version: '3.x' }
- run: flutter pub get
- name: Decode keystore
run: echo "${{ secrets.KEYSTORE_BASE64 }}" | base64 --decode > android/app/keystore.jks
- name: Build AAB
run: flutter build appbundle --release
env:
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
- name: Upload AAB artifact
uses: actions/upload-artifact@v4
with: { name: app-release-aab, path: build/app/outputs/bundle/release/app-release.aab }
build-ios:
needs: test
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- uses: subosito/flutter-action@v2
with: { flutter-version: '3.x' }
- run: flutter pub get
- run: cd ios && pod install
- name: Build IPA
run: flutter build ipa --release --export-options-plist=ios/ExportOptions.plist
- uses: actions/upload-artifact@v4
with: { name: app-release-ipa, path: build/ios/ipa/*.ipa }
build-web:
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: subosito/flutter-action@v2
with: { flutter-version: '3.x' }
- run: flutter pub get
- run: flutter build web --wasm --base-href "/app/"
- uses: actions/upload-artifact@v4
with: { name: web-release, path: build/web/ }
release:
needs: [build-android, build-ios, build-web]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Create GitHub Release
uses: softprops/action-gh-release@v1
with:
generate_release_notes: true
draft: false
- name: Slack notify
uses: slackapi/slack-github-action@v1
with:
payload: '{"text": "ShopApp ${{ github.ref_name }} released to all platforms!"}'
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}
❓ よくある質問
Q GitHub Actionsの無料枠で十分ですか?
A オープンソースプロジェクトは無制限。プライベートプロジェクトは2000分/月 — クロスプラットフォームフルビルドで約30-40分、月に約50ビルド可能。
Q iOSビルドにはmacOSランナーが必要ですか?
A はい。XcodeはmacOS上でしか動作しません。GitHubはmacOSランナーを提供していますが、課金はLinuxの10倍。CodemagicでmacOS分を節約できます。
Q 署名キーはどう安全に保管すべきですか?
A GitHub Secretsを使用(キーストア/証明書はBase64エンコード)、リポジトリには絶対にコミットしないでください。Secret scanningを有効にしてください。
Q Flutterビルドのキャッシュで高速化するには?
A
actions/cacheでpub-cacheとgradle-cacheをキャッシュ — ビルド時間が5分から2分に短縮。Q FastlaneとGitHub Actionsはどう連携しますか?
A GitHub Actionsがビルドをトリガーし、Fastlaneが署名とアップロードを処理。FastlaneはmacOSランナー上で実行するのが最も便利。
Q 本番バージョンのロールバックはどうしますか?
A Google Play Consoleは前バージョンへのロールバックをサポート、App Storeは新バージョンの提出が必要。CI/CDですべての過去のビルド成果物を保持してください。
📖 まとめ
- GitHub ActionsがFlutter CI/CDの第一選択 — YAML設定 + マルチマトリックスビルド
- Android署名はGitHub SecretsにBase64エンコードされたキーストアを保存
- iOSビルドにはmacOSランナーが必要、証明書はSecretsに保存
- Fastlaneがスクリーンショット撮影とTestFlight/Play Consoleへのアップロードを自動化
- 完全パイプライン:テスト → ビルド → 署名 → アップロード → 通知
📝 練習問題
- 基本 (⭐):GitHub Actions PRチェックワークフローを設定してください:flutter analyze + flutter test。
- 中級 (⭐⭐):Android AABビルドステップを追加し、GitHub Secretsで署名キーを管理し、ビルド後にアーティファクトをアップロードしてください。
- チャレンジ (⭐⭐⭐):完全なCI/CDパイプラインを実装してください:PRチェック(lint + テスト)+ タグトリガークロスプラットフォームビルド(Android AAB + iOS IPA + Web)+ 自動署名 + GitHub Release + Slack通知。