Flutter プロジェクトを GitHub Actions を使って AppStoreConnect へアップロードする
Flutter プロジェクトを手動でビルドして AppStoreConnect へアップロードしているのでいい加減なんとかしたいということで、GitHub Actions を使って自動化することに。
やりたいこと
- GitHub Actions をつかって Flutter プロジェクトをビルドして AppStoreConnect へアップロードする
- 自動署名(Automatically manage signing)は有効のまま
これらを前提としてあとは以下の特徴があるプロジェクト
- Firebase を採用している
- 広告SDKも採用している
GitHub Actions
基本的には上記記事の通り。
あとは少し改善できるところがあったので改善したのと、こちらのプロジェクト特有のもの(Firebaseなど)で必要になったステップを追加したぐらい。
軽く考え方を雑にまとめると以下の通り。
- Automatically manage signing を有効のまま
flutter build
を実行すると、マシンには署名情報がないのでエラーになる - なので
--no-codesign
オプションを付けてビルドをして後で手動で AppStoreConnect API を利用して Cloud Signing で署名する - ただ
--no-codesign
オプションをつけるとプッシュ通知の証明書が含まれない - そこで自分で Arcive にプッシュ通知の証明書を追加する必要がある
という流れ。
Flutter, GitHub Actions, AppStoreConnect という感じで雑に検索すると自動署名をOFFにして、プロビジョニングプロファイル何かを利用するというのが上がってくるけれど
さすがに今頃自動署名を切るのはありえないし、純粋に年一で更新が必要になるのも面倒くさいので「やりたいこと」にも書いた通り外せない条件。
そうなってくると以外に参考になる記事は少なかったりするので、本当に上記にあげた記事は助けられた。
そんなこんなで最終的に落ち着いたのが以下
name: Build and Deploy
on:
push:
branches: [ "main" ]
jobs:
deliver:
name: Build and Deploy to AppStoreConnect
runs-on: macos-latest
timeout-minutes: 20
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Switch Xcode version
run: sudo xcode-select -s /Applications/Xcode_16.2.app
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
channel: 'stable'
flutter-version: '3.24.4'
cache: true
- name: Install dependencies & run build_runner
run: |
flutter pub get
dart run build_runner build -d
- name: Setup Flutterfire CLI
run: dart pub global activate flutterfire_cli
# Cloud signingを使って署名するのでここでは no-codesign をつけてビルドだけ
- name: Build
run: flutter build ipa --no-codesign
# 署名せずに archive した場合プッシュ通知の証明書が含まれないので手動で追加する
- name: Add APNS Entitlements
run: |
codesign \
--entitlements ios/Runner/Runner.entitlements \
--force \
--sign "-" build/ios/archive/Runner.xcarchive/Products/Applications/Runner.app
# dSYMs を Artifacts にあげておく
- name: Upload dSYMs
uses: actions/upload-artifact@v4
with:
name: dsyms
path: build/ios/archive/Runner.xcarchive/dSYMs
retention-days: 7
- name: Extract App Store Connect API Private Key
env:
APPSTORECONNECT_API_KEY_ID: ${{ secrets.APPSTORECONNECT_API_KEY_ID }}
APPSTORECONNECT_API_AUTHKEY_P8: ${{ secrets.APPSTORECONNECT_API_AUTHKEY_P8 }}
run: |
mkdir -p ./private_keys
echo -n "$APPSTORECONNECT_API_AUTHKEY_P8" | base64 --decode --output ./private_keys/AuthKey_$APPSTORECONNECT_API_KEY_ID.p8
- name: Deploy AppStoreConnect with ExportOptions.plist
env:
APPSTORECONNECT_API_KEY_ID: ${{ secrets.APPSTORECONNECT_API_KEY_ID }}
APPSTORECONNECT_API_ISSUER_ID: ${{ secrets.APPSTORECONNECT_API_ISSUER_ID }}
run: |
xcodebuild -exportArchive \
-archivePath build/ios/archive/Runner.xcarchive \
-exportOptionsPlist ios/ExportOptions.plist \
-exportPath build/ios/ipa \
-allowProvisioningUpdates \
-authenticationKeyID $APPSTORECONNECT_API_KEY_ID \
-authenticationKeyIssuerID $APPSTORECONNECT_API_ISSUER_ID \
-authenticationKeyPath `pwd`/private_keys/AuthKey_$APPSTORECONNECT_API_KEY_ID.p8
要所にはコメントもあるのでなんのためのステップかはわかるだろうけれど、自分がハマったところや少し工夫したところを少し補足説明。
Xcode のバージョンを指定する
Flutter で広告も導入しているのだけれど、Xcode のバージョンが少し違うだけでビルドが通らなかったりするので、ローカルの環境と完全に一致させるために指定している。
- name: Switch Xcode version
run: sudo xcode-select -s /Applications/Xcode_16.2.app
Gtihub さんが用意している macOS にはデフォルトでいくつかのXcodeバージョンが入っているので結構切り替えられる。
ここ に macOS として指定できるイメージリストがあるのでそこを参照するといい。
みてて気づいたのが、Xcodes が導入されているんだっていうこと。今回は使わなかったけどいつかつかうかもしれない(笑)
Firebase用のセットアップ
Firebase を採用していて ios 向けのビルドをすると Build Phase に仕込まれている Script が動いて flutterfire
コマンドが必要になるので、それ用のセットアップが必要になる。
- name: Setup Flutterfire CLI
run: dart pub global activate flutterfire_cli
それがこれ。ちなみに Firebase CLI 自体はいらない。それとは違う話。
Flutter の iOS 向けビルド
上記にのせた記事だと build と archive を別ステップとして実行していたけれど、flutter コマンドでまとめられるのでそこはまとめる
# Cloud signingを使って署名するのでここでは no-codesign をつけてビルドだけ
- name: Build
run: flutter build ipa --no-codesign
flutter build ios
ではなくて flutter build ipa
までやると archive までやってくれる。
dSYMs のアップロード
これは必須ではない。何かのあれで Firebase Crashlytcs に dSYMs がアップロードできなかったときのために保険的に取っておいてるぐらい
# dSYMs を Artifacts にあげておく
- name: Upload dSYMs
uses: actions/upload-artifact@v4
with:
name: dsyms
path: build/ios/archive/Runner.xcarchive/dSYMs
retention-days: 7
AppStoreConnect API 利用まわり
yaml から離れて AppStoreConnect API を利用するときの注意点。
APIキーを生成するときのロールは admin であること。
必要以上な権限を与えたくないので、最初は developer から始めて次に appManager を試してみたけれど、いずれもアップロードステップでエラーになる。
developer ではそもそも API でアップロードできる権限がないし、 appManager では、Cloude signing を利用できないため。
とま、こんな感じでなんとかビルドからアップロードまで自動化できた。
他にも改善できるところとか、より良い方法があればぜひおしえてもらえると!(笑)