CircleCI と fastlane で iOS アプリのビルドを自動化する
Build サービスチームで Solution Architect をしている t_maru です。
前回はiOSアプリをビルドするために必要な証明書についてのお話をしました。
今回はこれらを使って自動ビルドを行うためにはどのような設定が必要になるのかお話します。
fastlane
CI に限ったツールではありませんが、iOS アプリ開発の際には fastlane と呼ばれるツールが使われることがあります。
このツールには iOS 開発と密接に関係のある証明書や Provisioning Profile などの自動管理やビルド時の自動取得・設定なども機能としてはあるため、適切に運用できれば開発者の手間や運用ミスを低減することができます。
ただし、Apple Developer Program に紐づいて iOS アプリを開発をしているチームが他にもいる場合は注意が必要です。
fastlane の証明書自動管理機能を使うと、指定されたストレージ (GitHub や Google Cloud Storage、S3 など) に暗号化された状態で証明書と Provisioning Profile が保管され、必要に応じて自動的に更新されるようになりますが、fastlane を使わずに Apple Developer Console から手動で各種証明書などをダウンロードして使用している場合 (証明書や Provisioning Profile が頻繁に更新されることを想定していない運用)、手動管理しているチームがアプリをビルドする際に使っていた証明書が他のチームの作業により更新されていると、ビルド時に失敗するといった状況も考えられますので法人内に複数の開発チームがありそれぞれ独自の運用をしている場合は気をつけていただければと思います。
fastlane を使って iOS アプリビルド時に証明書を自動取得させる
証明書の自動管理 (自動更新) については先程お伝えしたように社内事情で簡単に使える状況にない場合もあると思いますが、リポジトリに証明書を配置しておき、そこから fastlane 経由で自動的に証明書や Provisioning Profile をダウンロードすることは可能ですので、GitHub, CircleCI, fastlane を使った例を紹介したいと思います。
証明書を自動で取得するために使用する機能は fastlane の match という機能です。詳細は下記のドキュメントをご覧ください。
https://docs.fastlane.tools/actions/match/
今回は他のチームに影響を与えること無く fastlane match の機能を使うため、証明書、Provisioning Profile は既存のものを Apple Developer Console からダウンロードして使用することとします。
大まかな作業手順は下記の通りです。
証明書 (.cer)、秘密鍵 (.p12)、Provisioning Profile (.mobileprovision) を準備する
GitHub に証明書と Provisioning Profile 管理用の Private リポジトリを作成
fastlane match init コマンドを使って GitHub のリポジトリの URL などを設定
fastlane match import コマンドを使って用意した証明書類を GitHub リポジトリにアップロードする
Matchfile, Fastfile を変更して証明書類がビルド時に自動でダウンロードされるように設定する
CircleCI の設定を行う
環境変数に `MATCH_PASSWORD` を設定する
SSH の設定をする
以下、順を追ってそれぞれの手順を説明します。
証明書 (.cer)、秘密鍵 (.p12)、Provisioning Profile (.mobileprovision) を準備する
用意するものは以下の 3 点です。
証明書 (.cer)
秘密鍵 (.p12)
Provisioning Profile (.mobileprovision)
.cer と .mobileprovision は Apple の Developer Console からダウンロードしたものをそのまま利用可能です。
以下に参考の画面を掲載しますが、秘密鍵の .p12 ファイルに関しては証明書と秘密鍵をインストールされている Mac のキーチェーンアクセスから秘密鍵のみを選択した状態で書き出す必要があり、書き出す際もパスワードを設定しないようにご注意ください(fastlane がパスワード付きの .p12 ファイルに対応していないため)。
GitHub に証明書と Provisioning Profile 管理用の Private リポジトリを作成
今回は証明書類の保管先を GitHub としますので、GitHub 上に空の Private リポジトリを作成するだけで OK です。
作成したらリポジトリの SSH URL をメモとして控えておきます。
fastlane match init コマンドを使って GitHub のリポジトリの URL などを設定
開発で使用している PC 上で以下のコマンドを実行します。
※ 事前に Ruby の環境準備と fastlane コマンドのインストール、SSH で GitHub に接続するための設定が必要です。
bundle exec fastlane match init
実行した例が下記となります。ウィザードで聞かれるのは storage mode と、その storage の URL です。
処理が正常に終了すると `.fastlane/` ディレクトリに `Matchfile` というファイルが作成されます。
fastlane match import コマンドを使って用意した証明書類を GitHub リポジトリにアップロードする
次に、以下のコマンドを実行して証明書、Provisioning Profile などを設定していきます。
今回は Apple Developer Console へのアクセスを行わない方法 (Apple Developer Console へのアクセス権が与えられていない想定) で証明書等をアップロードしますので `skip_certificate_matching` オプションを使い、fastlane CLI による Apple Developer Console へのアクセスを省略します。
bundle exec fastlane match import --skip_certificate_matching true
このコマンドを実行し、PC に保管されている証明書のファイルパスなどすべての入力が終わると fastlane CLI が SSH 経由で指定された GitHub リポジトリに証明書、Provisioning Profile をアップロードしますが、アップロードの前に対話型のウィザードで聞かれたパスワードを使って暗号化処理されます。
※ 以下の例では、過去に fastlane match import の作業をしており、パスフレーズが Mac の Keychain Access に保存されているためウィザード上ではパスフレーズの入力が求められずに処理が完了しています。
Matchfile, Fastfile を変更して証明書類がビルド時に自動でダウンロードされるように設定する
今回は App Store 用ビルドを実施しますので Matchfile に記載されている type を `appstore` に変更します (デフォルトは development が設定されます)。
また、アプリの Bundle ID も `app_identifier` 項目で設定します。
# Matchfile の例
git_url("<GitHub リポジトリの SSH URL>")
storage_mode("git")
type("appstore") # The default type, can be: appstore, adhoc, enterprise or development
app_identifier(["<iOS アプリの Bundle ID>"])
Fastfile ではアプリを gym にてビルドする前に match を使って証明書を取得するように記載します。ここでのポイントは `readonly` を true で設定することです。こうすることで match を使用している場合でも証明書の更新は行わずに、リポジトリからの取得のみを行うようになります。
lane :appstore_build do
match(type: "appstore", readonly: true)
gym(
workspace: "sample-app.xcworkspace",
scheme: "sample-app",
output_directory: "Archives",
output_name: "sample-app-#{ENV['CIRCLE_BUILD_NUM']}.ipa",
export_method: "app-store"
)
end
CircleCI の設定を行う
最後に CircleCI の設定を行いますが、まずは `fastlane match import` コマンドを実施した際に入力した証明書等の暗号化用パスフレーズをプロジェクトの環境変数として `MATCH_PASSWORD` という名前で登録します。
次にプロジェクトの `SSH Keys` というメニューを開き `User Key` を登録します。
User key を使う場合、特定リポジトリに対しての権限ではなく、指定したユーザーの持っている権限がそのまま割り当てられることになりますので、場合によってはアクセスする必要のないリポジトリへのアクセス権を含んでしまう可能性がありますのでご注意ください。
この権限問題を解決する方法の 1 つとして、GitHub のマシンユーザーを作るという方法がありますので興味のある方は調査してみてください。
下記、CircleCI の参考ドキュメントです。
https://circleci.com/docs/ja/github-integration/#controlling-access-via-a-machine-user
権限問題を解決するもう一つの方法として GitHub の Deploy key と CircleCI の Additional SSH key を使う方法もありますが、こちらの方法についてはまた別途機会がありましたら触れさせていただきますので今回は省略させていただきます。
以上、一通り設定が完了したら変更を commit し GitHub に Push することで、CircleCI で設定した条件に従って workflow がトリガーされた際に fastlane match を使用して証明書を自動取得するようになります。
以下、CircleCI の設定ファイルのサンプルを掲載します。
version: 2.1
commands:
prepare-build:
steps:
- run:
name: Set Timezone
command: |
sudo systemsetup -settimezone "Asia/Tokyo"
- restore_cache:
keys:
- v1-gems-{{ checksum "Gemfile.lock" }}
- run:
name: Bundle install
command: bundle check || bundle install
environment:
BUNDLE_JOBS: 4
BUNDLE_RETRY: 3
- save_cache:
key: v1-gems-{{ checksum "Gemfile.lock" }}
paths:
- vendor/bundle
jobs:
build-appstore:
macos:
xcode: "14.3.0"
resource_class: macos.x86.medium.gen2
shell: /bin/bash --login -eo pipefail
environment:
BUNDLE_PATH: vendor/bundle
LC_ALL: en_US.UTF-8
LANG: en_US.UTF-8
steps:
- checkout
- prepare-build
- run:
name: Run tests
command: make test-all
- store_artifacts:
path: test_output
destination: test_output
- run:
name: Build ipa
command: bundle exec fastlane appstore_build --verbose
- store_artifacts:
path: Archives
destination: archives
workflows:
build-appstore-wf:
jobs:
- build-appstore:
filters:
branches:
only: main
まとめ
今回は CircleCI で iOS アプリをビルドするための方法の 1 つとして、fastlane match の機能を使って必要な証明書類を自動ダウンロードする例をご紹介しました。
CircleCI の公式ドキュメントでも fastlane match を使った方法が案内されておりますが、既存の証明書をインポートする方法の記載はありませんでしたので、具体的な手順付きで説明させていただきました。
fastlane match を使用する場合、証明書、秘密鍵、Provisioning Profile を GitHub リポジトリなどにアップロードする必要がありますが、リポジトリは Private でよく、リポジトリ上に保管されるファイルもアップロード前に暗号化されますのでセキュリティ上も大きな問題とはならないと思いますが、どうしてもリポジトリへのアップロードは行えないという場合に取れる手段として、手動で証明書の設定を行う方法を次回の記事でご紹介します。