見出し画像

Github Actions と fastlane で iOS アプリをアーカイブしてビルドを提出する

概要

Github Actions と fastlane を使って iOS アプリをアーカイブしてストアにアップロードしてみました。

Firebase App Distribution を使ってベータ版を配信したりする記事は結構ありますが、リリース前まで持っていくのはなかったので手順を残しておきます。

注: Github Actions の制限の関係もあるので、Private なリポジトリで試す時には無料枠に注意してください。

Secrets に設定するもの

証明書やパスワードなどリポジトリに直接コミットしたくない物はリポジトリの Secrets に設定して Github Actions から参照できるようにします。

Secrets の説明にある通り Github Actions からのみ参照されるっぽいですが、Public なリポジトリで使用する際は自己責任でお願いします 🙇‍♂️

シークレットは、暗号化され、選択したアクションにのみ公開される環境変数です。 このリポジトリへの共同編集者アクセス権を持つユーザーは、ワークフローでこれらのシークレットを使用できます。

シークレットは、フォークからのプルリクエストによってトリガーされるワークフローには渡されません。

※ リポジトリの Secrets にある説明を翻訳

今回設定した物は以下の通りです。

■ CERTIFICATE

リリースに必要な証明書です。キーチェーンから書き出します。
証明書はバイナリになっているようなので、BASE64 エンコードしてテキストにしたものを設定します。ワークフロー内でデコードして使います。

スクリーンショット 2020-07-20 17.12.42

# 書き出した証明書を BASE64 エンコードする
# テキストファイルに文字列がずらっと並んでいるので、それをまるっとコピーして貼り付けます
openssl base64 -in <書き出した証明書の名前>.p12 -out certificate_base64.txt

本来この辺りは match を使う方がスマートに管理できると思いますが、
今回はサクッと試したかったので Secrets に登録してしまっています。

■ CERTIFICATE_PASSWORD

証明書をキーチェーンから書き出した際に設定したパスワードです。

スクリーンショット 2020-07-20 17.13.21

■ PROVISIONING_PROFILE

アプリに紐づいているプロファイルです。
これも BASE64 エンコードして、ワークフロー内でデコードします。

■ KEYCHAIN_PASSWORD

キーチェーンで使用するパスワードです。
一時的なものなのでなんでもいいみたいです。

■ APPLE_ID

本来 fastlane の Appfile とかに記述する Apple ID です。
なんとなくコミットしたくなかったので Secrets に登録しています。
AppFile 等で既に設定してある場合は不要です。

■ FASTLANE_PASSWORD

後述する fastlane の deliver で Apple ID を使ってログインするので、
その際に使用する Apple ID のパスワードを設定します。

■ FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD

Apple の 2FA で使用する App 用パスワードを設定します。
App 用パスワードの発行については公式のサポートを参照してください。

■ FASTLANE_SESSION

fastlane 上で Apple の 2FA を通すために使用します。
App 用パスワードを発行し、以下のコマンドを実行することで得られる値を設定します。

fastlane spaceauth -u xxxxx@xxx.com
このコマンドは Apple ID の ログインセッションを取得していて、セッションが切れる度にこのコマンドを叩いて値を更新する必要があるみたいです。
どのくらいでセッションが切れるかまでは観測できていませんが、代替案として 2FA を設定していないアカウントを使用するという方法もあるみたいです。

Fastfile

fastlane にはアプリをアーカイブしてビルドをアップロードする部分をやってもらいます。

■ 前準備

まずはアーカイブするために必要な証明書とプロファイルを CI の環境にインポートするレーンを用意します。

private_lane :import_certificates_and_provisioning_profile do
   create_keychain(
       name: "ios_app_keychain",
       password: ENV["KEYCHAIN_PASSWORD"],
       timeout: 1800
   )
   import_certificate(
       certificate_path: "distribution.p12",
       certificate_password: ENV["CERTIFICATE_PASSWORD"],
       keychain_name: "ios_app_keychain",
       keychain_password: ENV["KEYCHAIN_PASSWORD"]
   )
   install_provisioning_profile(path: "distribution.mobileprovision")
end

■ アプリのアーカイブとアップロード

先ほど作成したレーンを実行して証明書等をインポートした後に、
アーカイブとビルドのアップロードを行います。

アーカイブには gym 、アップロードには deliver を使っています。

deliver を使ってメタデータ( アプリの説明文など )を一緒にアップできるみたいでしたが、今回は特に管理していないのでスキップしました。

lane :release do
   import_certificates_and_provisioning_profile
   build_app(
     scheme: "ビルドするアプリのスキーム",
     export_options: {
       method: "app-store"
     }
   )
   deliver(
     username: ENV["APPLE_ID"],
     app_identifier: "アプリのバンドルID",
     submit_for_review: false,
     force: true,
     skip_screenshots: true,
     skip_metadata: true
   )
 end

Github Actions のワークフロー

以下のワークフローでは master にプッシュされたタイミングで設定した Secrets を読み込んだり、CocoaPods や Carthage のキャッシュの設定などを行っています。

自分は CocoaPods と Carthage の両方を使用していたので、ここでは両方とも設定しています。

name: Swift

on:
 push:
   branches: [ master ]

jobs:
 build:

   runs-on: macos-latest

   steps:
   # 1. ソースのチェックアウト
   - uses: actions/checkout@v2

   # 2. 証明書を Secrets から取得
   - name: Setup p12 certificate file
     run: |
       echo "${{ secrets.CERTIFICATE }}" > distribution.p12.txt
       base64 --decode distribution.p12.txt > distribution.p12
   
   # 3. アプリ本体用のプロファイルを Secrets から取得
   - name: Setup provisioning profile
     run: |
       echo "${{ secrets.PROVISIONING_PROFILE }}" > distribution.mobileprovision.txt
       base64 --decode distribution.mobileprovision.txt > distribution.mobileprovision
   
   # 4. CocoaPods のキャッシュの設定
   - uses: actions/cache@v2
     with: 
       path: Pods
       key: ${{ runner.os }}-pods-${{ hashFiles('**/Podfile.lock') }}
       restore-keys: |
         ${{ runner.os }}-pods-
   
   # 5. キャッシュから Pods ファイルを取得、なければそのまま pod install
   - name: Install CocoaPods frameworks and build with custom scripts
     if: steps.cache-cocoapods.outputs.cache-hit != 'true'
     run: pod install

   # 6. Carthage のキャッシュの設定
   - uses: actions/cache@v2
     with:
       path: Carthage
       key: ${{ runner.os }}-carthage-${{ hashFiles('**/Cartfile.resolved') }}
       restore-keys: |
         ${{ runner.os }}-carthage-
   
   # 7. キャッシュから Carthage 関連のファイルを取得、なければそのまま carhage bootstrap
   - name: Install Carthage frameworks
     run: carthage bootstrap --platform iOS --cache-builds

   # 8. アーカイブして ipa をアップロード
   - name: Upload a new build to App Store Connect
     env:
       FASTLANE_PASSWORD: ${{ secrets.FASTLANE_PASSWORD }}
       FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD: ${{ secrets.FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD }}
       FASTLANE_SESSION: ${{ secrets.FASTLANE_SESSION }}
       KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
       CERTIFICATE_PASSWORD: ${{ secrets.CERTIFICATE_PASSWORD }}
       APPLE_ID: ${{ secrets.APPLE_ID }}
     run: fastlane release

最後に

CI を設定したことで、自分の Mac でアーカイブする作業がなくなり、より開発に集中できるようになりました。

実装してあとは指定のブランチにプッシュするだけなのでめちゃくちゃ楽です😎

参考にさせていただいた記事

[iOS] GitHub Actionsでfastlaneのmatchを使わずにAdHoc書き出しをしてからFirebase App Distributionにアップロードする

GitHub Actions+Firebase App DistributionでiOSアプリをAd hoc配布するための構成例

BitriseでFastlane Pilotを実行してiOSアプリをアップロードする際に2FAでエラーが発生した時の対応

この記事が気に入ったらサポートをしてみませんか?