🍏

DeploygateからTestFlightに移行した話:GitHub Actionsを用いたCI/CD環境構築

に公開

はじめに

こんにちは、助太刀 iOSチームリーダーの喜多です!

元々弊社では検証用アプリのデプロイ環境にGitHub Actions × Deploygateを用いていました。
しかし、この度Deploygateの旧プラン廃止を機にGitHub Actions × TestFlightに移行する方針にしました。

そこで今回は検証環境のCD環境をTestFlightに移行した際のワークフローやポイントを共有させていただきます。

Deploygate利用時のCDの仕組み

Deploygateを利用していた場合の自動Deployの仕組みは以下でした。

元々の仕組み

iOSチームではブランチ管理でGit-flowを用いて開発をしており、開発者はreleaseブランチにマージするだけで、自動的に検証用アプリがDeploygateにアップロードされ、チームメンバーにSlackで通知される流れが実現されていました。
今回TestFlightに移行後も、releaseブランチにマージするだけで自動でTestFlightにアップロードされ、Slackに通知されるという流れは変更しないようにしました。

TestFlight移行後のCDの仕組み

TestFlightに移行した後の自動Deployの仕組みは以下になります。

移行後の仕組み

Deploygateと比較すると、主な違いは以下の通りです:

  • 証明書管理: 手動管理 → 自動署名(Automatically manage signing)
  • 配布先: Deploygate → TestFlight
  • 認証方式: Deploygate API → App Store Connect API

実際に行った対応

以下の対応を行いました。

  • 証明書管理を手動管理→自動署名(Automatically manage signingを有効化)に変更
    → 建前:Fastlaneを使用することなくCD環境を構築するため(本音:煩雑な証明書管理から解放されるため😭)
  • ワークフローの修正
  • 既存のSlack通知の部分等は流用して、ビルド〜デプロイまでの部分を新規で作成しました。
  • 実際のワークフローは後述で記載しています。

TestFlightへの自動配信のワークフロー

前提

以下を前提としています。

ワークフロー

実際のワークフローは以下です。
Slack通知等の本記事に対して不必要な箇所は載せてません。
プロジェクト名やキー名等は適宜読み替えてください。

ワークフロー
name: iOS TestFlight Staging CI

# ワークフローのトリガー設定
on:
 # 手動実行を許可
 workflow_dispatch:
 # releaseブランチへのPRがマージされた時に実行
 pull_request:
   branches:
     - release
   types:
     - closed  # PRがマージまたはクローズされた時

jobs:
 # メインのビルドジョブ
 build:
   # macOSの指定 
   runs-on: macos-15

   steps:
     # リポジトリのチェックアウト
     - name: Checkout
       uses: actions/checkout@v4

     # Xcodeのセットアップ(ここは利用しているXcodeを指定 現時点ではXcode16.3を使用してる)
     - name: Set up Xcode 16.3
       uses: maxim-lobanov/setup-xcode@v1
       with:
         xcode-version: '16.3'

     # Ruby環境のセットアップ(現時点ではCocoaPodsを使用してるので、使用してないならば不要)
     - name: Set up Ruby
       uses: ruby/setup-ruby@v1
       with:
         ruby-version: '2.7.7'  # CocoaPods対応
         bundler-cache: true    

     # CocoaPodsのキャッシュ設定(ビルド時間短縮、CocoaPodsを使用してないならば不要)
     - name: Cache CocoaPods
       uses: actions/cache@v3
       with:
         path: Pods
         key: ${{ runner.os }}-pods-${{ hashFiles('**/Podfile.lock') }}
         restore-keys: |
           ${{ runner.os }}-pods-

     # CocoaPods依存関係のインストール(CocoaPodsを使用してないならば不要)
     - name: Install CocoaPods
       run: pod install --verbose

     # Xcodeアーカイブの作成
     # CODE_SIGNING_ALLOWED=NOでコード署名を無効化
     - name: Archive
       run: |
         xcodebuild archive -workspace [プロジェクト].xcworkspace -scheme "[スキーマ名]" -archivePath [プロジェクト].xcarchive -sdk iphoneos -configuration "[対象のConfiguration名]" CODE_SIGNING_ALLOWED=NO
   
     # entitlementsの埋め込み
     - name: Embed entitlements
       run: |
         codesign \
         --entitlements [プロジェクト]/[プロジェクト].entitlements \
         --force \
         --sign "-" [プロジェクト].xcarchive/Products/Applications/[プロジェクト].app

     # IPAエクスポート用の設定ファイル作成
     # EXPORT_OPTIONSはBase64エンコードされたplistファイルの内容
     - name: Create ExportOptions.plist
       run: |
         echo '${{ secrets.EXPORT_OPTIONS }}' > ExportOptions.plist
         cat ExportOptions.plist

     # Apple API Keyの復号化と配置
     # App Store Connect API認証用の秘密鍵を作成
     - name: Create Private Key
       run: |
         mkdir private_keys
         echo -n '${{ secrets.APPLE_API_KEY_BASE64 }}' | base64 --decode > ./private_keys/AuthKey_${{ secrets.APPLE_API_ISSUE_ID }}.p8

     # アーカイブからIPAファイルをエクスポート
     # App Store Connect APIを使用して自動署名
     - name: Export IPA
       run: |
         xcodebuild -exportArchive -archivePath [プロジェクト].xcarchive -exportOptionsPlist ExportOptions.plist -exportPath [プロジェクト].ipa -allowProvisioningUpdates -authenticationKeyPath `pwd`/private_keys/AuthKey_${{ secrets.APPLE_API_ISSUE_ID }}.p8 -authenticationKeyID ${{ secrets.APPLE_API_KEY_ID }} -authenticationKeyIssuerID ${{ secrets.APPLE_API_ISSUE_ID }}

     # App Store Connectへのアップロード
     # altoolを使用してTestFlightに配信
     - name: Upload to App Store Connect
       run: |
         xcrun altool --upload-app -f [プロジェクト].ipa/[プロジェクト].ipa -t ios -u ${{ secrets.APPLE_ID }} -p ${{ secrets.APP_APP_SPECIFIC_PASSWORD }}

ポイント

  • 「entitlementsの埋め込み」部分の箇所がないと、アーカイプ時に.entitlementsの内容が埋め込まれないのでアプリによってはpush通知が受け取れなくなったり、ユニバーサルリンクが機能しなくなりますので注意してください!
  • 恐らく上記ワークフローをベースにAIエージェントにお願いすると環境に適したものを作ってもらえると思います(良い時代になった☺️)

最後に

TestFlightに移行したことでCD環境のコスト削減をすることができました。
CI/CD環境構築で躓くことも多いと思うので、少しでも助けになったら嬉しいです!

助太刀では一緒に開発してくれるメンバを募集してます!
少しでもご興味を持っていただけたら下記よりお気軽にご連絡ください!

助太刀テックブログ

Discussion