モバイルCI/CDとベータ配信
手作業のビルド・アップロードから解放され、コミットからストア審査提出までを一本のパイプラインで自動化する。Fastlaneを軸にした設計原則と、TestFlight・内部テスト配布の勘所を押さえる。
- モバイルCI/CDの核はビルド番号の単調増加・署名資産の安全な注入・配信APIの冪等な呼び出しの3点で、Fastlaneはこれらをlaneという手続きの単位にまとめて再現可能にする。
- ベータ配信はTestFlight(iOS)と内部テストトラック(Android)が中心で、審査の有無・反映速度・テスター上限が異なるため、パイプラインは配信先ごとにlaneを分けて段階的に対象を広げる。
- ストア申請の自動化はメタデータ・スクリーンショット・審査提出までAPIで完結できるが、審査そのものは人手を含むため、パイプラインは提出までを担いリリース公開は段階的ロールアウトで制御する。
なぜモバイルのCI/CDは特殊なのか
Webのデプロイが「成果物をサーバーに配置して終わり」なのに対し、モバイルはビルド後に必ず外部ゲートウェイ(App Store Connect/Google Play)を経由し、その先に審査というアプリ側では制御できない工程が挟まる点が決定的に違います。加えてビルドには署名資産が必須で、CI上の一時的な環境にそれを安全に持ち込む必要があります。つまりモバイルのパイプラインは「ビルド」「署名」「配信API呼び出し」「ストア申請」という性質の異なる工程を、再現可能かつ冪等につなぐことが設計の主題になります。署名・プロビジョニングの内部原理そのものは /mobile-development/mobile-app-signing-provisioning/ に譲り、ここではパイプライン設計に焦点を当てます。
Fastlaneの基本モデル:laneとアクション
Fastlaneは、ビルドや配信といった個々の操作を「アクション」として提供し、それを順に並べた手続きを「lane」という単位で定義するツールです。laneは冪等な再実行を意図した手順書であり、CIから呼ばれるエントリポイントになります。
# Fastfile(iOSの例・概念を示す簡略版)
lane :beta do
increment_build_number(build_number: latest_testflight_build_number + 1)
build_app(scheme: "MyApp", export_method: "app-store")
upload_to_testflight(skip_waiting_for_build_processing: true)
end
重要なのは、laneが単なるシェルスクリプトの寄せ集めではなく、状態を持たない再現可能な手続きとして設計される点です。同じコミット・同じ署名資産を与えれば、何度実行しても同じ成果物と同じ配信結果に収束するべきで、ここが崩れると「CIでは通るのにローカルで失敗する」類の不安定さを生みます。
App Store ConnectもGoogle Playも、ビルド番号の重複を拒否します。ただしスコープが異なり、iOSのbuild number(CFBundleVersion)は同一のマーケティングバージョン内で一意であればよいのに対し、AndroidのversionCodeはアプリ全体で単調増加する整数で、過去にアップロードした最大値より必ず大きくする必要があります。したがってパイプラインはアップロードのたびに番号を必ず増やす必要があります。ローカルのカウンタに頼ると複数マシン・並列実行で衝突するため、latest_testflight_build_numberのように配信先の最新値を問い合わせて+1する、あるいはCIの実行番号(ビルドIDの単調増加)を採用するのが堅実です。番号の一意性はパイプラインが保証すべき不変条件だと捉えます。
署名資産をCIへ安全に注入する
CIは使い捨ての環境なので、証明書の秘密鍵とプロビジョニングプロファイルを毎回持ち込む必要があります。これを手作業でコピーするのは漏洩リスクも再現性も最悪です。iOS向けにはFastlaneのmatchが定番で、署名資産を暗号化して専用リポジトリ(またはクラウドストレージ)に保管し、CI上で復号して一時キーチェーンへ展開する方式を取ります。
| 方式 | 仕組み | 向いている状況 |
|---|---|---|
| match(同期方式) | 暗号化した証明書・プロファイルを共有ストアに置き、全マシンで同一資産を復号して使う | チーム開発・複数CIランナーで署名資産を統一したい |
| 手動インポート | CIのシークレット機構に.p12とプロファイルを格納し、都度キーチェーンへインポート | 資産の共有をせず、単一パイプラインで完結させたい |
| App Store Connect API Key | パスワード認証の代わりにJWT署名の鍵で認証し、2要素認証を回避 | 配信・審査提出のAPI呼び出し全般(match/upload双方で推奨) |
いずれの方式でも原則は共通です。第一に、秘密鍵や復号パスフレーズはコードやログに残さずCIのシークレット機構(環境変数・秘密ストア)経由でのみ渡します。第二に、CI上で作る一時キーチェーンはジョブ終了時に破棄し、ランナーに署名資産を残さないようにします。第三に、対話的なパスワード入力を要求する認証(Apple IDの2要素認証など)はCIで詰まるため、App Store Connect API Keyのような非対話認証へ置き換えます。
CIのログは往々にしてPull Requestや第三者から閲覧可能です。Fastlaneや各種コマンドが冗長モードでパスフレーズ・トークン・証明書パスを標準出力に書き出すと、そこから資産が漏れます。シークレットはマスク対象としてCIに登録し、デバッグ出力の粒度を上げるときも秘密情報が混ざらないことを確認します。署名資産が漏れると第三者が同一のBundle IDで正規署名されたバイナリを作れてしまうため、影響は単なるビルド失敗では済みません。
ベータ配信:TestFlightと内部テストトラック
ビルドができたら、審査前のテスターへ届ける段階です。ここはコード署名とは別レイヤーの「誰にどの段階で配るか」というガバナンスの問題で、iOSとAndroidで反映速度と審査の有無が異なります。
横にスクロール
| 配信先 | 審査 | 反映速度 | 上限とパイプライン設計 |
|---|---|---|---|
| TestFlight 内部テスター(iOS) | 不要 | ビルド処理完了後すぐ | App Store Connectのメンバー最大100名。upload_to_testflightで完結し、beta laneの標準ターゲット |
| TestFlight 外部テスター(iOS) | 簡易審査あり | 審査通過後 | 最大1万名。初回ビルドや大きな変更で審査が挟まるため、内部より配信までのリードタイムを見込む |
| 内部テストトラック(Android) | 不要 | 数分で反映 | 登録メールアドレスへ即時配布。upload_to_play_store(track: 'internal')で送り、Closed/Openへ昇格して対象を拡大 |
パイプライン設計としては、配信先ごとにlaneを分けるのが基本です。日々の開発ビルドは反映の速い内部チャネル(TestFlight内部/Android内部テスト)へ自動配信し、ここは審査を挟まないので短いフィードバックループが回せます。外部テスターやより広いトラックへの昇格は、マージやタグ付けなど明示的なトリガーに紐づけ、対象範囲を段階的に広げます。テスト対象アプリがオフライン同期を持つ場合は、配信したベータ版で /mobile-development/mobile-offline-first-sync/ が扱う競合解決の挙動を実機検証する、といった具合にベータ配信は本番前の最後の実機確認レイヤーとして機能します。
TestFlightへのアップロード後、Apple側でバイナリの処理(暗号化コンプライアンスの確認やシンボル処理)に数分〜十数分かかります。skip_waiting_for_build_processingで処理完了を待たずにlaneを終わらせると、CIジョブの占有時間を短縮できます。ただし外部テスターへ自動で回す場合は処理完了が前提になるため、「アップロードするlane」と「処理完了後にテスターへ配布するlane」を分離すると、CIの実行時間とリリース制御を両立できます。
ストア申請の自動化と段階的ロールアウト
本番リリースも、メタデータ登録・スクリーンショット差し替え・審査提出までをAPIで自動化できます。Fastlaneではdeliver(iOS)/upload_to_play_store(Android)が、多言語のメタデータやスクリーンショットをローカルの決められたディレクトリ構造から読み取り、一括で反映します。テキストや画像をGit管理下に置けるため、ストア掲載情報もコードレビューの対象にできるのが利点です。
ただし自動化できるのは提出までです。審査自体は人手のプロセスを含み、所要時間も結果もパイプラインの外側にあります。したがって成熟したパイプラインは「審査提出」と「一般公開」を明確に分けます。Androidの段階的ロールアウト(staged rollout)は公開直後の配信比率を数%に絞り、クラッシュ率などの指標を見ながら比率を上げていく仕組みで、iOSにも段階的リリースの相当機能があります。パイプラインは審査通過後に低い比率で公開を開始し、指標が健全なら比率引き上げ、異常があればロールアウト停止、という制御を担います。
審査を通っても、それは「ストアのポリシーに反しない」ことの確認にすぎず、アプリが実機で正常動作する保証ではありません。署名検証やレビューをすり抜けたクラッシュ・回帰が、公開直後に全ユーザーへ一気に広がると被害が最大化します。段階的ロールアウトで初期比率を絞り、クラッシュフリー率やANR率が閾値を割ったら即座にロールアウトを止められる状態を保つことが、リリース事故の被害を限定する最後の砦です。
パイプライン全体の設計原則
工程をつなぐと、ビルド → 署名 → テスト → ベータ配信 → 審査提出 → 段階公開という一本の流れになりますが、これを1つの巨大なlaneに詰め込むのは避けます。各工程を独立したlaneに分け、CI側のジョブ依存関係で連結すると、失敗した工程だけを再実行でき、責務も明確になります。
- 早い工程で速く失敗させる:ビルド番号の採番・署名資産の展開・単体テストのような軽い検証を前段に置き、コストの高いアーカイブビルドや配信を後段に回す。
- 配信APIは冪等に扱う:同一ビルド番号の再アップロードは失敗する前提で、リトライ時は採番からやり直すか、既存ビルドの存在を確認してスキップする。
- 回帰の自動検知を挟む:ベータ配信の前段に、起動時間やバイナリサイズの計測を組み込み、/mobile-development/mobile-app-startup-optimization/ で扱うようなパフォーマンス回帰を数値で検知する。
- 人手の判断点を明示する:外部テスター昇格や一般公開の比率引き上げは、自動化しつつも承認ゲート(手動トリガー)を残し、いつでも止められるようにする。
まとめ
モバイルCI/CDは、Webと違ってビルドの先に外部ゲートウェイと審査が必ず控えるという構造上、「ビルド・署名・配信・申請」という性質の異なる工程を再現可能につなぐことが本質です。Fastlaneはこれらをlaneという冪等な手続きに束ね、ビルド番号の単調増加・署名資産の安全な注入・配信APIの一貫した呼び出しを自動化の土台にします。ベータ配信はTestFlightと内部テストトラックを配信先ごとにlaneで分け、審査の有無と反映速度の違いを踏まえて対象を段階的に広げます。ストア申請は提出まで自動化しつつ、審査という外部工程を尊重して一般公開は段階的ロールアウトで制御する——この「自動化できる境界」を正しく見極めた設計が、手作業のリリースにつきまとう属人性と事故リスクを構造的に減らします。
モバイル開発の記事ガイド
モバイルCI/CDとベータ配信を実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
モバイル
比較で見る軸
難易度: advanced / カテゴリ: モバイル開発 / タグ数: 6
導入後に効く点
ベータ配信はTestFlight(iOS)と内部テストトラック(Android)が中心で、審査の有無・反映速度・テスター上限が異なるため、パイプラインは配信先ごとにlaneを分けて段階的に対象を広げる。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- advanced
- カテゴリ
- モバイル開発
- タグ数
- 6
判断チェックリスト
- 自社の用途が「モバイル / CI/CD」に近いか確認する。
- 強みである「モバイルCI/CDの核はビルド番号の単調増加・署名資産の安全な注入・配信APIの冪等な呼び出しの3点で、Fastlaneはこれらをlaneという手続きの単位にまとめて再現可能にする。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。