🚀

[Flutter]GitHub ActionsでApp Distributionにアプリをアップロードした

2024/05/19に公開2

概要

Firebase Projectに関連付けているFlutterアプリをDev/Prodの環境分けをしつつ、App Distributionにデプロイする。

やりたいこと

  • iOS, Android両方をアップロードしたい
  • FirebaseのプロジェクトをDev,Prodと使い分けてDistributionにアップロードするのはDevにしたい
  • analyze & testもやっておきたい
  • 無料で
  • 一応パブリックリポジトリとして公開もしたい(GitHub Actionsが無料になったりするし)、なのでセキュアな情報は見られたくない

GitHub Actionsのメリット

無料(制限あり)

月10,000分
※iOSのビルドで使うmacOSのランナーはLinuxの10倍の分消化をする

いろんなアクションがOSSで転がってる

ビルドやアップロードなどをサポートしてくれる(内部では地道にコマンドを実行している)ものが結構あり、引数さえちゃんとすればいい感じにやってくれる。

各フェーズでやること

前提

GitHub Actionsのスクリプトは.github/workflowsに格納する
スクリプトはyamlで書く

test & analyze

以下のことをします。
ファイル名 : analayze_test.yaml

  1. repositoryをcheckout
  2. Javaをセットアップ
  3. Flutterをセットアップ
  4. Packageをセットアップ
  5. Firebase Projectをセットアップ
  6. analyze と testを実施

以下一式です。

name: analyze & test
on:
  push:
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-version: "3.19.5"
      - 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

3のFlutterをセットアップの手順までは、Actionを呼び出すだけなので省略します。

4. Packageをセットアップ

プラグインを取得、コード自動生成をするためにflutter pub getbuild_runnerを実行します。

- name: Setup packages
  run: |
    flutter pub get
    dart run build_runner build -d

5. Firebase Projectをセットアップ

Firebase関連のプロジェクトのセットアップを行います。

- 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 CLIのインストールを行います。

curl -sL https://firebase.tools | bash

dartのflutterfire_cliを有効にします。

dart pub global activate flutterfire_cli

こうすることで、flutterfireのコマンドが実行できるようになります。

そして、flutterfire configureを実行します。

flutterfire configure -p $FIREBASE_PROJ_DEV_NAME -y --platforms "ios, android" -i "com.example.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

ローカルで実行すれば分かりますが、flutterfire configureは対話形式でFlutterプロジェクトとFirebase Projectに関連づけるコマンドです。

ただCI上いちいち対話しているわけにもいかないので、色々引数をつけています。

-p $FIREBASE_PROJ_DEV_NAME ・・・ Firebaseで用意しているプロジェクト名$FIREBASE_PROJ_DEV_NAMEはGitHub Actionsの環境変数に設定しています。
-y ・・・ 確認系の対話をスキップします。
--platforms "ios, android" ・・・ 配信するプラットフォームを指定します。`"ios, android"`でiOSとAndroidを指定しています。
-i/-a/-m/-w/-x "パッケージ名" ・・・ i=iOS a=Android m=macOS w=Web x=Windowsのパッケージ名を指定します。上記の--platformsにプラットフォームを指定していても、-mや-xなどのパッケージ名を引数に入れてとエラーが出るので、各プラットフォームごとのパッケージ名を入れています。(使わないのはテキトウでOK)
-t $FIREBASE_AUTO_TOKEN ・・・ `firebase login:ci`で取得したログイントークン。本来`firebase login`でログインをする必要があるのですが、事前にトークンを取得することでログインの手順をスキップしています。
-f ・・・ 既に`flutterfire configure`を実行しているプロジェクトの場合、設定を上書きするコマンド。
> null ・・・ これをつけることで、flutterfireを実行した際の出力を見れないようにしています。実行時にfirebaseのユニークIDが表示されてしまっていたので表示されないようにしました。

これらの引数でflutterfireのコマンドを実行することで、lib/firebase_options.dartGoogle-Service-Info.plistなどのファイルをCheckoutしたプロジェクトに組み込んでいます。

6. analyze と testを実施

最後にflutter analyzeflutter testを実行することで、解析とテストを実行することができます。

- name: run analyze
  run: flutter analyze
- name: run test
  run: flutter test

これでanalyzeとtestは完了です。

iOSビルド&デプロイ

以下のことをします。
ファイル名 : ios_build_stg.yaml

  1. (macOSのランナー)repositoryをcheckout
  2. 証明書をキーチェーンに登録
  3. Flutterをセットアップ
  4. Packageをセットアップ
  5. Firebase Projectをセットアップ
  6. ipaファイルを生成
  7. Artifactをアップロード
  8. 登録したキーチェーンを削除
  9. (Linuxのランナー)repositoryをcheckout
  10. Artifactをダウンロード
  11. Firebase Distributionにアップロード

1,3,4,5は「test & analyze」と同じことをするので省略します。
以下、一式です。

name: "[DEV] Build and Publish iOS"
on:
  push:
jobs:
  build:
    runs-on: macos-latest

    steps:
      - name: Checkout repository
        uses: actions/checkout@v2
      - 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-version: "3.19.5"
      - 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" -i "com.example.example-app" -a "com.example.example_app" -m "com.example.pedometer-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 --build-number=$GITHUB_RUN_NUMBER
      - name: collect ipa artifacts
        uses: actions/upload-artifact@v2
        with:
          name: release-ipa
          path: build/ios/ipa/*.ipa
      - 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@v2
      - name: Get release-ipa from artifacts
        uses: actions/download-artifact@v2
        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: admin
          file: pedometer_app.ipa
          releaseNotes: $GITHUB_REF_NAME

2. 証明書をキーチェーンに登録

iOSのビルドを行うにはDistributionの証明書やProvisioningProfileが必要になるので、ビルドのランナーに紐づけていきます。

- 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

以下の部分で、環境変数に設定しているp12ファイルとmobileprovisionファイルをランナー内に生成します。

    # 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

BUILD_CERTIFICATE_BASE64はp12ファイルをBase64エンコードをして、その文字列をGitHub Actionsの環境変数にセットします。
また、BUILD_PROVISION_PROFILE_BASE64も同じくmobileprovisionファイルをBase64エンコードをして、その文字列をGitHub Actionsの環境変数にセットします。

Base64をしてエンコードをするには以下のコマンドでBase64にエンコードした状態でクリップボードに貼り付けます。

base64 -i {自分の証明書}.p12 | pbcopy
base64 -i {自分のProvisioningProfile}.mobileprovision | pbcopy

security create-keychainなどを使ってキーチェーンに登録します。
P12_PASSWORDはp12ファイルを作成した時に設定したパスワードを環境変数としてセットしています。
KEYCHAIN_PASSWORDは以下で作成したキーチェーンのパスワードです。そのため不要とあれば環境変数にする必要はなく適当の文字を設定しても大丈夫です。

ProvisioningProfileは特定のフォルダに移動させます。

    # 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

この状態でビルドを行うと登録している証明書や保存したProvisioningProfileが参照されることになります。

6. ipaファイルを生成

flutterのbuildコマンドでipaを生成します。
--export-options-plistという引数でExportOptions.plistというファイルを参照させます。

- 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 --build-number=$GITHUB_RUN_NUMBER

https://zenn.dev/tsukatsuka1783/articles/flutter_build_ipa
バイナリをExportで出力させるとフォルダ内に入っています。
参照するProvisioningProfileやteamIDなどXcodeで設定している内容が記載されているファイルです。
これをflutterのbuildコマンドに設定することで、どういう方法や設定でipaを出力するかを指定することができます。

7. Artifactをアップロード & 8. 登録したキーチェーンを削除

ビルドをしてipaの出力が完了したら、macOSのランナーからlinuxのランナーに切り替えます。
そのため、一度Artifactとしてipaファイルをアップロードします。
そして登録したキーチェーンと保存していたProvisioningProfileを削除します。
if: ${{ always() }}とすることで、上記までのタスク上で失敗した際にも必ずクリア処理が実行されるようになっています。

- name: collect ipa artifacts
  uses: actions/upload-artifact@v2
  with:
    name: release-ipa
    path: build/ios/ipa/*.ipa
- 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

11. Firebase Distributionにアップロード

9と10のArtifactを取得する手順については特出すべきことはやっていないので、省略します。

以下でFirebase Distributionにipaファイルをアップロードします。
appIdはFirebaseのiOSのIDで1:000000000000:ios:zzzzzzzzz0000000000000みたいな形式のIDです。
serviceCredentialsFileContentはFirebaseの認証トークンになります。
GCPにて作成する必要がありこちらを参照ください。
またgroupsに設定しているadminについては、FirebaseのDistributionで設定したテスターグループをセットします。
グループと指定していない場合は、アップロード後に誰に配信するかを設定する必要があります。

- 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: admin
    file: pedometer_app.ipa

これでiOSアプリのアップロードは完了です。

Androidビルド&デプロイ

以下のことをします。
ファイル名 : android_build_stg.yaml

  1. repositoryをcheckout
  2. Flutterをセットアップ
  3. Packageをセットアップ
  4. Firebase Projectをセットアップ
  5. keystoreの設定
  6. apkファイルを生成
  7. Firebase Distributionにアップロード

全体は以下です。

name: "[DEV] Build and Publish Android"
on:
  push:
jobs:
  # Build job
  build:
    runs-on: ubuntu-latest
    steps:
      - name: setup repository
        uses: actions/checkout@v4
      - uses: actions/setup-java@v4
        with:
          distribution: "temurin"
          java-version: "11.x"
      - name: setup flutter
        uses: subosito/flutter-action@v2
        with:
          channel: stable
          flutter-version: "3.19.5"
      - 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.example.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: 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
      - name: Build APK
        run: flutter build apk --release --build-number=$GITHUB_RUN_NUMBER
      - 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: admin
          file: ./build/app/outputs/apk/release/app-release.apk
          releaseNotes: $GITHUB_REF_NAME

1,2,3,4については、他の項目でと同じなため省略します。
Android特有の作業としては、キーストアのセットアップがあります。

5. keystoreの設定

- 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

AndroidStudioにてjksというファイルと作成します。詳細はこちら
作成したjksファイルをbase64でエンコードした文字列をGitHubActionsの環境変数にセットします。(ANDROID_KEY_JKS)
ここで設定した、キーストア自体のパスワード(ANDROID_STORE_PASSWORD)、Alias(ANDROID_KEY_ALIAS)、Aliasに紐付けたパスワード(ANDROID_ALIAS_PASSWORD)をGitHubActionsの環境変数にセットします。

それらを、android/key.propertiesというファイルに書き出していきます。

そして、AndroidはCIのスクリプト以外にbuild.gradlekey.propertiesの情報を参照し署名するといった記載をする必要があります。

まずはファイルの読み込みをします。
def keystorePropertiesFile = rootProject.file("key.properties")
で読み込めます。

...(省略)

def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
    flutterVersionName = '1.0'
}

def keystorePropertiesFile = rootProject.file("key.properties")

android {
    namespace "com.example.example_app"
    compileSdk flutter.compileSdkVersion
...(省略)

その後読み込んだファイルのデータで署名をするように、
android/signingConfigs/releaseに以下のように設定します。

そして、buildTypes/releasesigningConfigに設定する値をsigningConfigs.release(今ファイルを読み込んで反映させた設定)をセットします。

...(省略)
        versionCode flutterVersionCode.toInteger()
        versionName flutterVersionName
    }

    signingConfigs {
        release {
            if (keystorePropertiesFile.exists()) {
                def keystoreProperties = new Properties()
                keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
                keyAlias keystoreProperties['keyAlias']
                keyPassword keystoreProperties['keyPassword']
                storeFile file(keystoreProperties['storeFile'])
                storePassword keystoreProperties['storePassword']
            }
        }
    }

    buildTypes {
        release {
                    signingConfig signingConfigs.release
        }
    }
...(省略)

これでAndroidのreleaseビルド時にandroid/key.propertiesのデータを読み込み、署名しつつビルドされることになります。

6. apkファイルを生成

Androidのアプリ出力はiOSよりかは簡素で、以下のコマンドのみでapkファイルを出力します。
aabでも出力やアップロードもできますが、FirebaseとGoolePlayConsoleを連携させなければいけないため、今回はapkでやります。(詳細はこちら)

- name: Build APK
  run: flutter build apk --release --build-number=$GITHUB_RUN_NUMBER

7. Firebase Distributionにアップロード

出力されたapkファイルをDistributionにアップロードします。
引数の詳細はiOSと同じですが、appIdはFirebaseのプロジェクトにある1:000000000000:android:zzzzzzzzzzz00000000000のような形式のIDです。

- 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: admin
    file: ./build/app/outputs/apk/release/app-release.apk

実行の順番

今のままだとanalyze & testiOSのビルド``Androidのビルドが同時に実行されてしまいます。
analyzetestが失敗した場合はビルドまで行かなくていいので、フローを作成します。

こんな感じのフロー

analyze & test --- > iOS Build --- > iOS Deploy
                |
                -- > Android Build

まずは各ファイル(analyze_test.yaml, android_build_stg.yaml, ios_build_stg.yaml)で以下のようになっているところを変更します。

on:
  push:

on:
  workflow_call:

このように変更することで、スクリプトの実行タイミングをpush時から、とこかのworkflowで呼ばれて実行されるようになります。

また、workflow_callにすることで、GitHubActionsの環境変数を直接参照できなくなりました。
そのため、環境変数を引数のように受け取るよう修正をしていきます。

analyze_test.yaml

on:
  workflow_call:
    secrets:
      FIREBASE_PROJ_DEV_NAME:
        description: プロジェクトID
        required: true
      FIREBASE_AUTH_TOKEN:
        description: Firebaseのトークン情報
        required: true

ios_build_stg.yaml

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

android_build_stg.yaml

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

これで今までの3ファイルで実装したスクリプトをその他のスクリプトから呼ぶ準備ができました。
そして最終的に build_deploy_stg.yamlというファイルを作成し、それぞれ環境変数をsecretsというパラメータで各スクリプトに渡すようにします。

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 }}

以下のようにneedsanalyze_and_testを指定することで、Androidのビルド、iOSのビルドがそれぞれanalyze_and_testの後に実行されることになります。

  android_build_stg:
    needs: [analyze_and_test]
    uses: ./.github/workflows/android_build_stg.yaml
...
  ios_build_stg:
    needs: [analyze_and_test]
    uses: ./.github/workflows/ios_build_stg.yaml

実行すると以下のようになります。

やってみて

意外とFirebase x Flutter(iOS, Android両方) x GitHub Actionsの組み合わせで一通りのCIサンプルみたいなものがなかったので、色々な記事を参考にしてできるようになったので記録用に記事にしました。

参考にさせてもらった記事たち 🙇

https://takamii.hatenablog.com/entry/2021/07/02/151453
https://flutter.salon/build/no_team_id/
https://qiita.com/uhooi/items/a17a5d0e5dd5a76191ac
https://sussan-po.com/2022/01/14/ios-update-automation/
https://zenn.dev/korosuke613/scraps/dc18529983a95e
https://kojirooooocks.hatenablog.com/entry/2023/01/22/130347
https://note.com/shcahill/n/nb0b810d47eb6
https://www.docswell.com/s/yorifuji/KGXMNM-2023-10-26-011249
https://zenn.dev/flutteruniv_dev/articles/a4576f96002dae

Discussion

h_hh_h

記事がとても参考になりました。圧倒的感謝です!

一点だけ気になったのが、>が一つ足りないのでこのままのコピペだとエラーになりますね。

echo 'storeFile=release.jks' > android/key.properties

shima999bashima999ba

コメントありがとうございます!
改めて確認してみたのですが、手元だと>が一個で問題なさそうでした 🤔
もしかすると別の原因でエラーになっているかもです
(例えば、android/key.propertiesが元々存在し、上記のコマンドで上書きしちゃったとか)

> : ファイルを上書きする
>> : ファイルに追記をする