はじめに
今回は GithubActions+Firebase App Distribution で CI/CD パイプラインの構築とテスト配布を行っていきます。本記事は後編のため前編を見てからご覧ください。サンプルプログラムはGitHubで公開中。(シリーズで随時更新していくのでたまにエラーがあるかも)
前編ではGitHub Actionsでのワークフローの実行に必要な環境変数の収集とセットを行いました。後編では実際にワークフローを書いてパイプラインを動かします。
ワークフローの作成
実際にGitHub Actionsで実行するワークフローのコードを書きます。コードはこの後載せていますが各行の解説は参考記事を見てください。参考記事からの変更点と個別で設定が必要な箇所はコメントアウトで示してあります。
ファイル作成
以下のフォルダ構成を構築する。.github
とworkflow
も新規作成。
flutter_sample
├── .github
│ └── workflow
│ ├── build_deploy_stg.yaml
│ ├── analyze_test.yaml
│ ├── ios_build_stg.yaml
│ └── android_build_stg.yaml
build_deploy_stg.yaml
全体を統括する役目を果たします。
name: "build & deploy stg app"
on:
push:
jobs:
analyze_and_test:
uses: ./.github/workflows/analyze_test.yaml
secrets:
FIREBASE_PROJ_DEV_NAME: ${{ secrets.FIREBASE_PROJ_DEV_NAME }}
FIREBASE_AUTH_TOKEN: ${{ secrets.FIREBASE_AUTH_TOKEN }}
android_build_stg:
needs: [analyze_and_test]
uses: ./.github/workflows/android_build_stg.yaml
secrets:
FIREBASE_PROJ_DEV_NAME: ${{ secrets.FIREBASE_PROJ_DEV_NAME }}
FIREBASE_AUTH_TOKEN: ${{ secrets.FIREBASE_AUTH_TOKEN }}
ANDROID_KEY_JKS: ${{ secrets.ANDROID_KEY_JKS }}
ANDROID_STORE_PASSWORD: ${{ secrets.ANDROID_STORE_PASSWORD }}
ANDROID_ALIAS_PASSWORD: ${{ secrets.ANDROID_ALIAS_PASSWORD }}
ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
FIREBASE_DEV_ANDROID_ID: ${{ secrets.FIREBASE_DEV_ANDROID_ID }}
FIREBASE_DEV_TOKEN: ${{ secrets.FIREBASE_DEV_TOKEN }}
ios_build_stg:
needs: [analyze_and_test]
uses: ./.github/workflows/ios_build_stg.yaml
secrets:
APPSTORE_CERT_BASE64: ${{ secrets.APPSTORE_CERT_BASE64 }}
APPSTORE_CERT_PASSWORD: ${{ secrets.APPSTORE_CERT_PASSWORD }}
MOBILEPROVISION_ADHOC_BASE64: ${{ secrets.MOBILEPROVISION_ADHOC_BASE64 }}
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
FIREBASE_PROJ_DEV_NAME: ${{ secrets.FIREBASE_PROJ_DEV_NAME }}
FIREBASE_AUTH_TOKEN: ${{ secrets.FIREBASE_AUTH_TOKEN }}
FIREBASE_DEV_IOS_ID: ${{secrets.FIREBASE_DEV_IOS_ID}}
FIREBASE_DEV_TOKEN: ${{secrets.FIREBASE_DEV_TOKEN}}
EXPORT_OPTIONS: ${{ secrets.EXPORT_OPTIONS_STG }}
analyze_test.yaml
コード解析とテストを行います。テストの書き方については別記事で書きます。
name: analyze & test
on:
workflow_call:
secrets:
FIREBASE_PROJ_DEV_NAME:
description: プロジェクトID
required: true
FIREBASE_AUTH_TOKEN:
description: Firebaseのトークン情報
required: true
jobs:
analyze:
runs-on: ubuntu-latest
steps:
- name: setup repository
uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
distribution: "temurin"
java-version: "21"
- name: setup flutter
uses: subosito/flutter-action@v2
with:
channel: stable
# 現在のFlutterのバージョン flutter doctor や flutter --versionで確認
flutter-version: "3.27.0"
- name: check version
run: flutter --version
- name: Setup packages
run: |
flutter pub get
dart run build_runner build -d
- name: Setup Firebase Project
env:
FIREBASE_PROJ_DEV_NAME: ${{ secrets.FIREBASE_PROJ_DEV_NAME }}
FIREBASE_AUTH_TOKEN: ${{ secrets.FIREBASE_AUTH_TOKEN }}
run: |
curl -sL https://firebase.tools | bash
dart pub global activate flutterfire_cli
flutterfire configure -p $FIREBASE_PROJ_DEV_NAME -y --platforms "ios, android" -i "com.exmple.example-app" -a "com.example.example_app" -m "com.example.example-app" -w "1:XXX:web:YYY" -x "1:XXX:web:YYY" -t $FIREBASE_AUTH_TOKEN -f > null
- name: run analyze
run: flutter analyze
- name: run test
run: flutter test
ios_build_stg.yaml
iOSのビルドとアップロードを行います。
name: analyze & test
on:
workflow_call:
secrets:
APPSTORE_CERT_BASE64:
description: 証明書Base64
required: true
APPSTORE_CERT_PASSWORD:
description: 証明書のパスワード
required: true
MOBILEPROVISION_ADHOC_BASE64:
description: プロビジョニングプロファイル(Adhoc)
required: true
KEYCHAIN_PASSWORD:
description: 一時的なKeychainのパスワード
required: true
FIREBASE_PROJ_DEV_NAME:
description: プロジェクトID
required: true
FIREBASE_AUTH_TOKEN:
description: Firebaseのトークン
required: true
FIREBASE_DEV_IOS_ID:
description: FirebaseのiOS ID
required: true
FIREBASE_DEV_TOKEN:
description: FirebaseのJson Token
required: true
EXPORT_OPTIONS:
description: Export Options
required: true
jobs:
build:
runs-on: macos-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install the Apple certificate and provisioning profile
env:
BUILD_CERTIFICATE_BASE64: ${{ secrets.APPSTORE_CERT_BASE64 }}
P12_PASSWORD: ${{ secrets.APPSTORE_CERT_PASSWORD }}
BUILD_PROVISION_PROFILE_BASE64: ${{ secrets.MOBILEPROVISION_ADHOC_BASE64 }}
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
run: |
# create variables
CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12
PP_PATH=$RUNNER_TEMP/build_pp.mobileprovision
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
# import certificate and provisioning profile from secrets
echo -n "$BUILD_CERTIFICATE_BASE64" | base64 --decode --output $CERTIFICATE_PATH
echo -n "$BUILD_PROVISION_PROFILE_BASE64" | base64 --decode --output $PP_PATH
# create temporary keychain
security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
security set-keychain-settings -lut 21600 $KEYCHAIN_PATH
security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
# import certificate to keychain
security import $CERTIFICATE_PATH -P "$P12_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
security list-keychain -d user -s $KEYCHAIN_PATH
# apply provisioning profile
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
cp $PP_PATH ~/Library/MobileDevice/Provisioning\ Profiles
- name: Flutter get
uses: subosito/flutter-action@v1
with:
channel: stable
# 現在のFlutterのバージョン
flutter-version: "3.27.0"
- name: Setup packages
run: |
flutter pub get
dart run build_runner build -d
- name: Setup Firebase Project
env:
FIREBASE_PROJ_DEV_NAME: ${{ secrets.FIREBASE_PROJ_DEV_NAME }}
FIREBASE_AUTH_TOKEN: ${{ secrets.FIREBASE_AUTH_TOKEN }}
run: |
curl -sL https://firebase.tools | bash
dart pub global activate flutterfire_cli
flutterfire configure -p $FIREBASE_PROJ_DEV_NAME -y --platforms "ios, android" -i "com.studiomk.flutterSample" -a "com.studiomk.flutter_sample" -m "com.example.example-app" -w "1:XXX:web:YYY" -x "1:XXX:web:YYY" -t $FIREBASE_AUTH_TOKEN -f > null
- name: Building IPA
env:
EXPORT_OPTIONS: ${{ secrets.EXPORT_OPTIONS }}
run: |
echo $EXPORT_OPTIONS > ios/Runner/ExportOptions.plist
flutter build ipa --release --export-options-plist=ios/Runner/ExportOptions.plist
- name: collect ipa artifacts
#v2からv4にアップデートされていた
uses: actions/upload-artifact@v4
with:
name: release-ipa
path: build/ios/ipa/*.ipa
if-no-files-found: error
- name: Clean up keychain and provisioning profile
if: ${{ always() }}
run: |
security delete-keychain $RUNNER_TEMP/app-signing.keychain-db
rm ~/Library/MobileDevice/Provisioning\ Profiles/build_pp.mobileprovision
release:
name: Release ipa to Firebase
needs: [build]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Get release-ipa from artifacts
#v2からv4にアップデートされていた
uses: actions/download-artifact@v4
with:
name: release-ipa
- name: Upload artifact to Firebase App Distribution
uses: wzieba/Firebase-Distribution-Github-Action@v1
with:
appId: ${{secrets.FIREBASE_DEV_IOS_ID}}
serviceCredentialsFileContent: ${{secrets.FIREBASE_DEV_TOKEN}}
#テスターグループ名(個別)
groups: sample
#プロジェクト名.ipa(個別)
file: flutter_sample.ipa
releaseNotes: $GITHUB_REF_NAME
android_build_stg.yaml
Androidのビルドとアップロードを行います。
name: "[DEV] Build and Publish Android"
on:
workflow_call:
secrets:
FIREBASE_PROJ_DEV_NAME:
description: プロジェクトID
required: true
FIREBASE_AUTH_TOKEN:
description: Firebaseのトークン情報
required: true
ANDROID_KEY_JKS:
description: キーストアファイル
required: true
ANDROID_STORE_PASSWORD:
description: キーストアファイルのパスワード
required: true
ANDROID_ALIAS_PASSWORD:
description: キーストアファイルのALIASのパスワード
required: true
ANDROID_KEY_ALIAS:
description: キーストアファイルのALIAS名
required: true
FIREBASE_DEV_ANDROID_ID:
description: Firebase上のAndroid ID
required: true
FIREBASE_DEV_TOKEN:
description: FirebaseのJsonの認証情報
required: true
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: setup repository
uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
# Javaのバージョンは17を推奨される
distribution: "temurin"
java-version: "17"
- name: setup flutter
uses: subosito/flutter-action@v2
with:
channel: stable
#現在のFlutterのバージョン
flutter-version: "3.27.0"
- name: check version
run: flutter --version
- name: Setup packages
run: |
flutter pub get
dart run build_runner build -d
- name: Setup Firebase Project
env:
FIREBASE_PROJ_DEV_NAME: ${{ secrets.FIREBASE_PROJ_DEV_NAME }}
FIREBASE_AUTH_TOKEN: ${{ secrets.FIREBASE_AUTH_TOKEN }}
run: |
curl -sL https://firebase.tools | bash
dart pub global activate flutterfire_cli
flutterfire configure -p $FIREBASE_PROJ_DEV_NAME -y --platforms "ios, android" -i "com.studiomk.flutterSample" -a "com.studiomk.flutter_sample" -m "com.example.example-app" -w "1:XXX:web:YYY" -x "1:XXX:web:YYY" -t $FIREBASE_AUTH_TOKEN -f > null
- name: Create key.properties
run: |
echo ${{ secrets.ANDROID_KEY_JKS }} | base64 -d > android/app/release.jks
echo 'storeFile=release.jks' > android/key.properties
echo 'storePassword=${{ secrets.ANDROID_STORE_PASSWORD }}' >> android/key.properties
echo 'keyPassword=${{ secrets.ANDROID_ALIAS_PASSWORD }}' >> android/key.properties
echo 'keyAlias=${{ secrets.ANDROID_KEY_ALIAS }}' >> android/key.properties
# --build-numberを消すとうまくいった
- name: Build APK
run: flutter build apk --release
- name: Upload apk to Firebase App Distribution
uses: wzieba/Firebase-Distribution-Github-Action@v1
with:
appId: ${{secrets.FIREBASE_DEV_ANDROID_ID}}
serviceCredentialsFileContent: ${{secrets.FIREBASE_DEV_TOKEN}}
#テスターグループ名(個別)
groups: sample
file: ./build/app/outputs/apk/release/app-release.apk
releaseNotes: $GITHUB_REF_NAME
実行
では実際にパイプラインを動かしてみましょう。
git add .
$ git commit -m "任意のメッセージ"
$ git push origin master
GitHub Actions
リポジトリのActions
に新しいワークフローが追加されているはずです。
単純なコードのエラーはanalyze_and_test
で、ビルド関連のエラーはios_build_stg
やandroid_build_stg
でキャッチされます。
Firebase App Distribution
次にFirebaseのコンソールにアクセスし、App Distributionを開きましょう。リリースの欄に追加されているはずです。
テストのダウンロード方法
- App Distributionの招待リンクタブから招待リンクを発行
- リンクを開きスマホのメアドを登録
- スマホから招待メールを開く
- Get startedボタンを押すとインストールが開始
- ホーム画面からアプリを開く
まとめ
これでFlutterのプロジェクトにCI/CDパイプラインとFirebase App Distributionによるテスト配布が導入できました。ご指摘、ご質問あればコメントにてお寄せください。
前編