はじめに:「コピペでは動かない」自動ビルドの世界へようこそ
まずは前提となる用語の超ざっくり予習
本題に入る前に、本文で繰り返し登場する用語を、「だいたいこういうもの」くらいの粒度で予習しておきましょう。エンジニアを始めて間もない方も、ここを押さえれば本編で迷子になりません。詳しい話は記事の本編で順を追って説明します。
| 用語 | これ何?(ざっくり) |
|---|---|
| GitHub | プログラムのコード(=ソースコード)を保管・共有するためのWebサービス。多くのエンジニアが共同作業で使う「コードのクラウド倉庫」のようなもの |
| GitHub Actions | GitHubが提供する自動化サービス。「コードが更新されたら、自動でビルドする・テストする・配布する」といった作業を、自分のPCを使わずにクラウド上で実行してくれる |
| CI(Continuous Integration、シーアイ) | 「コードを更新するたびに、自動でビルド・テスト・配布を行う仕組み」全般を指す言葉。GitHub Actions は、そのCIを実現するためのツールのひとつ |
| CIサーバー | CIで実際にビルド作業をしてくれるコンピューター。GitHub Actions の場合、Microsoft(GitHubの親会社)が用意してくれているクラウドのMacが使われる |
| ビルド | ソースコードから、実際にスマホで動くアプリのファイルを作る作業。「料理のレシピ(=コード)から実際の料理(=アプリ)を作る工程」のイメージ |
| yaml(ヤムル)ファイル | 「何を、どの順番でやってほしいか」を設定として書くテキストファイルの一種。GitHub Actions は、このyamlファイルに書かれた内容に従って動く |
| テスター | 完成前のアプリを試してもらう人。社内のメンバーや、招待した特定のユーザー |
ざっと押さえたところで、本題に入っていきましょう。
iOSアプリを開発していて、こんな場面に出会ったことはないでしょうか。
- 開発中のアプリをチームメンバーに渡したいけど、毎回手作業でビルドして配布するのが面倒
- 「GitHub Actions(コード更新のたびに自動でビルド・配布してくれる仕組み)で自動化しよう」と調べ始めたら、見慣れない単語の嵐に呆然とした
- ネットで見つけた設定ファイル(yaml)をコピペしたら、なぜか動かず、エラーメッセージで検索しても解決しない
iOSの自動ビルド環境(CI環境)の構築は、初学者がつまずく代表格として有名な領域です。検索して出てくるのはコピペ用の設定ファイルばかりで、「なぜそれが必要なのか」を解説してくれる記事がほとんどありません。
そのため、コピペで動かしては失敗し、別の設定に置き換えて、また失敗する……という終わらないループにハマる人が後を絶たない。これが iOS の自動ビルド環境構築の「典型的な挫折パターン」です。
なぜAI時代にこれを学ぶ意味があるのか
ここで疑問が浮かぶかもしれません。
「ChatGPTやClaudeに頼めば、設定ファイル(yaml)なんて秒で書いてくれる時代でしょ?」
その通りです。でも、ちょっと想像してみてください。
AIが出してくれた100行の設定ファイルを実行したら、エラーが出ました。エラーメッセージには provisioning profile not found(プロビジョニングプロファイルが見つからない)と書いてあります。あなたは、ここから何を直しますか?
「AIにもう一回聞けばいい」というのも一つの手です。しかし、AIも万能ではありません。同じ回答を繰り返したり、的外れな修正案を出してきたりすることがあります。
そんなとき、「ああ、これはプロビジョニングプロファイル(後で詳しく説明します。アプリを端末にインストールするための許可証のようなもの)を所定のフォルダに置けていないんだな」「ファイル名と中の設定が一致していないんだな」と、自分で原因を当てられる人こそ、AI時代に強いエンジニアです。
ゼロからコードを書く力ではなく、AIが出した答えを評価し、必要なら修正できる目。この記事は、その目を養うための「地図」です。
この記事を読み終えると得られるもの
この記事を最後まで読むと、次の3つができるようになります。
- 自動ビルド環境(iOS CI)に登場する用語(証明書、プロビジョニングプロファイル、P12、キーチェーンなど)が「何のためにあるのか」を自分の言葉で説明できる
- AIが生成した自動化の設定ファイルを読んで、各ステップが何をしているか追える
- エラーが出たとき、どのステップに原因があるか当たりを付けられる
手順書ではなく、「なぜこれが必要なのか」を理解するための地図として、ぜひ手元に置いて活用してください。
この記事で扱うもの(登場人物紹介)
まずは登場人物を一覧で押さえておきましょう。詳しい説明は本編で行うので、今は「ふーん、こんなのが出てくるんだ」くらいで大丈夫です。
| 登場するもの | ひとことで言うと |
|---|---|
| コード署名 | 「このアプリは安全です」という証明の仕組み |
| 証明書(Certificate) | 開発者の身分証明書 |
| P12ファイル | 証明書+秘密鍵をセットにした持ち運び用ファイル |
| プロビジョニングプロファイル | 「誰が作った、どのアプリを、どの端末に入れてOK」という許可証 |
| ExportOptions.plist | ビルド時の配布設定を書いたファイル |
| キーチェーン(Keychain) | macOSの鍵の金庫 |
| GitHub Secrets | CIに渡す機密情報の保管庫 |
| Base64エンコード | バイナリファイルをテキストに変換する方法 |
| Firebase App Distribution | テスターにアプリを配る仕組み |
困ったらこの表に戻ってきてください。
1. コード署名 — なぜ iOS だけこんなに面倒なのか
ここから本編です。まず最初に押さえるべきは「そもそもなぜ iOS の CI はこんなに大変なのか」という話。これがわからないと、以降の用語がすべて「ただの面倒な作業」に見えてしまいます。逆に、ここを理解すれば、全体が「ちゃんと意味のある仕組み」として見えてきます。
そもそも「アプリのファイル」って何?
最初に、超基本から確認させてください。
スマートフォンのアプリは、実は1つの「ファイル」として存在しています。Webサイトをダウンロードしてオフラインで開けるのと同じで、アプリのプログラムや画像をまとめたパッケージファイルがあって、それをiPhoneやAndroidが「読み込んで実行する」というイメージです。
そのファイル形式は、Android と iOS で違います。
- Android のアプリファイル → APK(Android Package Kit の略)
- iOS のアプリファイル → IPA(iOS App Store Package の略)
App Storeで「インストール」を押したとき、裏では「IPAファイルがダウンロードされて展開されている」と思ってください。
Android との違い
Android では、APKを作るときに「自分で作った印鑑(keystore)」で署名します。自分で印鑑を作って自由に押せる、町内会の回覧板のような世界です。Googleの許可は要りません。
一方 iOS は事情が違います。Apple が発行した印鑑(証明書)でしか署名できないのです。
Apple が「この開発者は信頼できる」と認め、印鑑を貸してくれて、その印鑑で署名したアプリだけが iPhone にインストールできる。「Appleの管理下にある世界」 という感じです。
イメージで理解する
なぜこんな仕組みがあるのか
iPhoneは「安全な環境」を売りにしています。怪しいアプリを誤ってインストールしてしまった、というトラブルを減らすために、Apple は次の3つを徹底的にチェックします。
- 誰が作ったか を証明書で確認する
- どのアプリか を Bundle ID(後述)で識別する
- どの端末にインストールしていいか をプロビジョニングプロファイル(後述)で制限する
この3重チェックを通らないと、iPhoneはアプリのインストールを拒否します。
「面倒くさい」と感じる仕組みは、たいてい「ユーザーを守るため」にあります。iOSの面倒くささは、iPhoneユーザーの安全と引き換えなのですね。
Bundle ID とは(先取り解説)
ここで「Bundle ID」という言葉が出てきました。本編で詳しくは扱いませんが、よく出てくるので簡単に。
Bundle ID は、アプリを世界で一意に識別するための文字列です。たとえばこんな形です。
com.example.myapp
com.apple.mobilesafari (Safari の Bundle ID)
com.google.Maps (Google Maps の Bundle ID)
会社のドメインを逆さにして、後ろにアプリ名を付ける、というのが慣習です。LINEもメルカリもApp Storeにいる無数のアプリが、それぞれ別のBundle IDを持っていて、iPhoneはこれを使ってアプリを区別しています。
2. 証明書(Certificate)— 開発者の「身分証明書」
Section 1で、Apple は「誰が作ったか」「どのアプリか」「どの端末か」の3重チェックをしている、という話をしました。ここからは、その3つを順番に見ていきます。
まず最初の 「誰が作ったか」 を担っているのが、これから説明する「証明書」です。
たとえ話:パスポート
証明書は、ずばりパスポートだと思ってください。
自作のパスポートでは入国できません。同じように、Apple非公認の証明書では署名できないのです。
証明書の種類
開発者が使う証明書には、用途別に2種類あります。
| 種類 | 役割 | 使う場面 |
|---|---|---|
| Apple Development | 開発用 | 自分のiPhoneでテストする時 |
| Apple Distribution | 配布用 | テスターやApp Storeに出す時 |
CI で Ad Hoc 配布(後述、テスターに配る方式)をするなら、必ず Apple Distribution を使います。「自分の開発機で動かす」のと「他の人に配る」のは別の権利として扱われている、と覚えてください。
公開鍵と秘密鍵 — ここが肝
証明書の裏側では、公開鍵暗号 という仕組みが動いています。
「公開鍵暗号」と聞くと身構えてしまいますが、ざっくりこう理解してください。
- 鍵が「2つで1セット」になっている
- 一方の鍵で「鍵を閉める」と、もう一方の鍵でしか「開けられない」
- 片方は世界中に公開してOK(公開鍵)
- もう片方は本人だけが大事に持つ(秘密鍵)
iOSの世界に当てはめると、こうなります。
公開鍵(.cer) → 「南京錠」のようなもの。誰でも入手できる
Apple Developer Portal からダウンロード可能
秘密鍵 → 「南京錠の鍵」のようなもの。本人だけが持つ
あとで説明する「CSR」を作成した Mac の
キーチェーンにだけ存在する
ここで「CSR」という用語がいきなり出てきましたが、これは「Appleに証明書をお願いする申請書」のことです(次の項目で詳しく説明します)。この申請書を作った瞬間に、Macの中で秘密鍵が自動生成される、という仕組みになっている、と今は理解してください。
アプリに署名するときは、秘密鍵で「確かに自分が作った」という電子署名を行います。iPhoneは公開鍵を使って、その署名が本物かどうかを検証します。
ここで大事なポイントを1つ。
秘密鍵を持っていない人は、その証明書を使って署名できません。
これを覚えておいてください。あとで「なぜP12(証明書と秘密鍵をまとめた持ち運び用ファイル。Section 3で詳しく説明します)が必要なのか」を理解するときに効いてきます。
CSR(証明書署名要求)とは
先ほど少し触れましたが、CSRは「Apple さん、私に証明書を発行してください」とお願いする申請書のようなものです。Certificate Signing Request の頭文字を取って CSR と呼ばれています。
順序はこうです。
ここで非常に重要なのは、「秘密鍵は CSR を作った Mac にしか存在しない」 ということです。
たとえば、CSRをAというMacで作成し、そこから発行された証明書をBというMacにダウンロードしてきても、Bでは署名できません。なぜなら、秘密鍵はAのMacのキーチェーンの中にしかないからです。Aで作った南京錠の鍵を、Bは持っていないイメージです。
これは、新しいMacに買い替えたときも同じです。Apple Developer Portalから証明書をダウンロードしてきても、新しいMacには秘密鍵がないので、署名できません。
そして、これと同じ問題がCIサーバーでも起きます。
ここで「CIサーバー」という言葉が出てきましたが、念のため整理しておきます。CI(Continuous Integration、継続的インテグレーション)とは、コードをpushしたら自動でビルドやテストを行う仕組みのこと。GitHub Actions はそのCIの仕組みを提供しているサービスのひとつで、裏で実際にビルド作業を行っているのが「CIサーバー」(GitHubが用意してくれているmacOSマシン)です。
このCIサーバーは、毎回まっさらな新品のMacが用意されるようなものなので、当然 秘密鍵を持っていません。そこで、秘密鍵もまとめて持ち込むために使うのが、次のSection 3で説明する P12ファイル です。
つまり、
- 別の Mac(買い替えた新しいMacや、CIサーバーなど)で同じ証明書を使いたい
- そのためには、秘密鍵もセットで持っていく必要がある
- そのためのファイル形式が P12(次のSection 3で詳しく説明します)
という流れになります。
3. P12ファイル — 証明書の「持ち運びパック」
Section 2の最後で、「秘密鍵はCSRを作ったMacにしか存在しない」「別のMacで使うにはP12が必要」という話をしました。
このSectionでは、その「P12って何者なのか」「なぜわざわざこの形式が必要なのか」を解きほぐしていきます。
なぜ P12 が必要なのか
ここで、CIの話に戻ります。
CIサーバー(GitHub Actions の macOS マシン)は、毎回まっさらな状態で起動します。ホテルにチェックインしたら、部屋に何もない状態と同じです。前回の利用者の荷物は残っていないし、自分のキーチェーンも持ち込まれていません。
つまり、証明書と秘密鍵の両方をCIサーバーに渡す必要があります。
ところが、Apple Developer Portal からダウンロードできる .cer ファイルには 公開鍵しか入っていません。署名に必要な秘密鍵はキーチェーンの中にしかないのです。
そこで登場するのが P12ファイル(PKCS#12形式)です。
たとえ話:パスポート再び
| ファイル | たとえると | 何ができる? |
|---|---|---|
.cer |
パスポートのコピー | 本人確認はできるが、本人の証明にはならない |
.p12 |
パスポート原本+指紋データ | これがあれば入国(署名)できる |
P12 の作り方
ローカルのMacで、次の手順を踏みます。
1. キーチェーンアクセス.app を開く
2. 証明書カテゴリから対象の証明書を探す
3. 右クリック → 「書き出す...」
4. フォーマット: 個人情報交換 (.p12) を選択
5. パスワードを設定(空でもOK、ただし設定推奨)
6. 保存
ここで「書き出す」がグレーアウトしている場合は、秘密鍵がキーチェーンにない可能性が高いです。
これは前述のとおり、CSR を作った Mac でしかエクスポートできないためです。原則として、その証明書を作ったMacを探すか、新しく作り直すしかありません。
4. プロビジョニングプロファイル — 「誰が・何を・どこに」の許可証
ここまでで、Apple の3重チェックのうち 「誰が作ったか」 を担う証明書(とその持ち運び形式であるP12)を扱いました。
しかし、証明書だけではアプリは iPhone にインストールできません。残りの 「どのアプリか」「どの端末にインストールしていいか」 を許可する仕組みが別途必要です。それが、このSectionで扱う「プロビジョニングプロファイル」です。
たとえ話:コンサートチケット
プロビジョニングプロファイルは、コンサートチケットのようなものです。
コンサートチケットには、こう書いてありますよね。
3つの構成要素
| 要素 | 何を指定しているか | 具体例 |
|---|---|---|
| App ID(Bundle ID) | どのアプリか | com.example.myapp |
| 証明書 | 誰が署名したか | Apple Distribution: 山田太郎 |
| デバイスリスト | どのiPhoneにインストールできるか | iPhone A, B, C の UDID |
「App ID」と「Bundle ID」がここで両方出てきましたが、ほぼ同じものです。Bundle ID を Apple Developer Portal に登録すると App ID として扱われる、くらいの認識でOKです。
配布方法と対応するプロファイルの種類
iOSアプリの配布方法は4種類あり、それぞれに対応するプロファイルがあります。これを知っておくと、「どのプロファイルを作ればいいか」迷わなくなります。
Firebase App Distributionで社外・社内テスターにiOSアプリを配る場合、多くのケースではAd Hoc配布を使います。
ウィジェットや拡張機能がある場合
ここでひとつ、ハマりがちなポイントを共有します。
iOSには「App Extension」という仕組みがあります。ホーム画面に置けるウィジェットや、共有メニューから呼び出せる Share Extension などがこれにあたります。
実は、これらの拡張機能はメインアプリとは別のアプリ扱いになります。つまり、別の Bundle ID を持ちます。
それぞれに対して別のプロビジョニングプロファイルを作成する必要があります。これを忘れると、ビルド時に署名エラーが大量発生して大変なことになります。「ウィジェットを追加したら急にビルドが落ちるようになった」という現象は、たいていこれが原因です。
UDID とは
「Ad Hoc 配布は登録済みのデバイスのみ」と書きましたが、そのデバイスを Apple に登録するためのIDが UDID です。
UDID(Unique Device Identifier)は iPhone 1台1台に割り振られた固有の識別番号で、世界に同じ番号を持つiPhoneは存在しません。マイナンバーのiPhone版、と思ってください。
確認方法はこんな感じです。
方法1: Mac に iPhone を接続
→ Finder でデバイスを選択
→ デバイス情報をクリックして表示
方法2: ターミナルで
xcrun xctrace list devices
このUDIDを Apple Developer Portal に登録した端末でないと、Ad Hoc配布ではインストールできません。
5. ExportOptions.plist — ビルドの「配布設定ファイル」
ここまでで、署名に必要な「材料」(証明書 = P12 と、プロビジョニングプロファイル)が揃いました。
次の問題は、CIサーバーに「この材料を使って、どの方法で配布するアプリを作って」と指示する方法です。普段はXcodeのGUIでポチっと選んでいるその選択を、CIではどう伝えればいいのか。それを担うのが ExportOptions.plist です。
なぜ必要なのか
普段Xcodeで手動ビルドしていると、こんな画面を見たことがあるはずです。アーカイブ後に「どの方法で配布するか」を選ぶ画面です。
[Ad Hoc] [App Store] [Development] [Enterprise]
↑ クリックで選ぶ
これは Xcode の GUI(画面)でポチっと選べるから簡単です。
しかしCIにはGUIがありません。マウスもキーボードも無い、コマンドラインだけの世界です。代わりに、ExportOptions.plist というファイルに設定を書いておくことで、同じことをコマンドラインから指定します。
GUIでポチっと押す代わりに、テキストファイルに書いておくイメージです。
plistファイルとは
ここで「plist」という用語が出てきましたが、これはApple独自の設定ファイル形式です。中身はXMLという形式で、人間が読み書きできるテキストファイルです。詳しい仕様を覚える必要はなく、「Appleの設定ファイル形式なんだな」程度でOKです。
ファイルの中身(Ad Hoc配布の場合)
<dict>
<!-- どの方法で配布するか -->
<key>method</key>
<string>ad-hoc</string>
<!-- Apple Developer のチームID -->
<key>teamID</key>
<string>XXXXXXXXXX</string>
<!-- 署名方式(CIでは manual 一択) -->
<key>signingStyle</key>
<string>manual</string>
<!-- Bundle ID ごとに使うプロビジョニングプロファイル名 -->
<key>provisioningProfiles</key>
<dict>
<key>com.example.myapp</key>
<string>MyApp AdHoc Profile</string>
</dict>
</dict>
signingStyle が manual なのはなぜ?
Xcode の「Automatic Signing」は、Xcode が Apple Developer Portal に自動接続してプロファイルを管理する仕組みです。便利ですが、Apple ID へのログインが必要です。
CIサーバーには Apple ID でログインできない(できたとしても、認証情報を保管するのが面倒で危険)ので、手動(manual)で「このプロファイルを使ってください」と明示的に指定する必要があります。
【ローカル開発】
Xcode → Apple ID ログイン → 自動でプロファイル取得
【CI】
GitHubActions → Apple ID ログイン不可 → 手動で指定
※CIではApple IDログインや自動署名に依存すると管理が複雑になりやすいため、この記事ではManual Signingで明示的に指定する前提で説明します。
6. キーチェーン(Keychain)— macOSの「鍵の金庫」
ここまでで、配布方法の指示書(ExportOptions.plist)も用意できました。
ところで、Section 3でP12ファイルを作りました。このP12をCIサーバーに送り込んだとして、「どこに置けば署名に使ってもらえるのか?」 という疑問が残ります。実はP12は、ただファイルとして置いておくだけでは使えません。macOSの「鍵管理システム」に登録する必要があるのです。それがキーチェーンです。
キーチェーンとは
macOS に搭載された鍵管理システムです。パスワード、証明書、秘密鍵などを暗号化して安全に保管します。
イメージはホテルの金庫。中身は安全だけど、開けるには認証が必要です。
実は、私たちが普段Macを使っているとき、SafariがWebサイトのパスワードを覚えていてくれたり、Gmailのアカウントが自動で保存されていたりしますが、あれもキーチェーンが裏で動いています。
ローカル開発では、証明書をダブルクリックすると自動的にキーチェーンに登録され、Xcode がそこから参照して署名します。開発者はキーチェーンの存在をほとんど意識しません。
CIでキーチェーンが問題になる理由
ところが、CIサーバーでは話が違います。
CIサーバーのキーチェーンは、まっさらな状態です。証明書を「キーチェーンに手動で登録する」作業が必要になります。ワークフロー内では、こんな手順を踏みます。
1. 一時的なキーチェーンを新規作成
→ なぜ一時的? デフォルトキーチェーンを汚さないため
2. キーチェーンをアンロック
→ ロックされたままだと証明書を取り出せない
3. P12ファイルをキーチェーンにインポート
→ これで署名に使える状態になる
4. キーチェーンを検索リストに追加
→ codesign コマンドがこのキーチェーンを見つけられるようにする
5. パーティションリストを設定
→ Xcode や codesign ツールにアクセス許可を与える
ビルド後は、セキュリティのためにキーチェーンごと削除します。「使ったら片付ける」を徹底するわけです。
security コマンドの意味
CI ワークフローでよく見る security コマンドの意味を整理します。ワークフローを読むときの手助けになるので、これを機にぜひ覚えてみて下さい!
| コマンド | やっていること |
|---|---|
security create-keychain -p "password" path |
新しいキーチェーンを作成 |
security unlock-keychain -p "password" path |
キーチェーンのロックを解除 |
security set-keychain-settings -lut 21600 path |
6時間(21600秒)自動ロックしない設定 |
security import cert.p12 -P "" -A -t cert -f pkcs12 -k path |
P12をインポート。-Aは全アプリからアクセス許可 |
security set-key-partition-list -S apple-tool:,apple: -k "pw" path |
Apple のツール群にアクセス権を付与 |
security list-keychain -d user -s path |
ユーザーのキーチェーン検索リストに追加 |
security delete-keychain path |
キーチェーンを削除(クリーンアップ) |
ちなみに codesign は、実際にアプリに署名を行うmacOSのコマンドです。Xcodeの裏側でも、ビルド時に codesign が呼び出されています。
7. GitHub Secrets と Base64 — 機密ファイルをCIに渡す方法
ここまで読んできて、ふと気になったかもしれません。
「そもそも、P12やプロビジョニングプロファイルって、どうやってCIサーバーに届けるの?」
P12には秘密鍵が入っています。プロビジョニングプロファイルにも署名情報が含まれます。これらを安全にCIに渡す方法、それがこのSectionのテーマです。
問題
CIサーバーにP12証明書やプロビジョニングプロファイルを渡したい。でも、これらは機密情報です。
ここで、絶対に守るべきルールがあります。
P12をGitHubのリポジトリにそのままpushしてはいけません。
なぜか。GitHubのリポジトリは、誰かに見られる可能性があります。Public(公開)リポジトリは当然世界中に見えていますし、Privateリポジトリでも、メンバー全員が中身を見られます。万が一P12と秘密鍵が漏洩すると、第三者があなたのアプリに署名できてしまうのです。アプリストアになりすましアプリが並ぶような事故にも繋がりかねません。
解決策:GitHub Secrets
そこで使うのが GitHub Secrets です。これは、リポジトリに紐づく暗号化された変数の保管庫です。
特徴を3つ挙げます。
- 登録後は管理画面からも値を確認できない(上書きのみ可能)
- ワークフローのログにも自動でマスクされる(
***と表示される) - ワークフローの中で
${{ secrets.SECRET_NAME }}と書くと値を取り出せる
「値を一度入れたら、もう人間には見えない。CIだけが使える」というイメージです。
※Secretsはログ上でマスクされますが、加工した値や一部だけを出力した値まで必ず保護されるとは限りません。CIログに機密値を出力しないことが前提です。
なぜ Base64 が必要なのか
ここで初学者がつまずくポイントです。
GitHub Secrets は テキスト しか保存できません。
でも、P12ファイルやプロビジョニングプロファイルはバイナリファイルです。
「バイナリファイル」というのは、画像や動画、実行ファイルのように、人間が直接読めない(メモ帳で開いても文字化けする)形式のファイルのことです。0と1のデータがそのまま並んでいるイメージです。テキストファイル(人間が読める文字だけのファイル)とは中身の構造が違います。
そこで Base64 という変換方式を使って、バイナリをテキスト文字列に変換してから Secrets に保存します。
Base64は「バイナリ→テキスト」「テキスト→バイナリ」を可逆的に変換できる仕組みです。圧縮や暗号化ではなく、「形を変えるだけ」 と覚えてください。
具体的なコマンド
# 【ローカルMacで実行】保存する時
base64 -i certificate.p12 | pbcopy
# → クリップボードにBase64テキストがコピーされる
# → GitHub の Secrets 設定画面に貼り付ける
# 【ワークフロー内】CI で復元する時
echo -n "${{ secrets.IOS_P12_CERTIFICATE_BASE64 }}" | base64 --decode -o cert.p12
# → テキストからバイナリに戻して cert.p12 ファイルを作る
iOS CI で必要な Secrets 一覧
| Secret名 | 中身 | 何に使う |
|---|---|---|
| P12証明書(Base64) | 署名用の証明書+秘密鍵 | CIサーバーのキーチェーンにインポート |
| P12パスワード | P12のパスワード | インポート時の認証 |
| プロビジョニングプロファイル(Base64) | ターゲットごとに1つ | 所定フォルダに配置して署名時に参照 |
| Firebase App ID | Firebaseのアプリ識別子 | アップロード先の指定 |
| Firebase サービスアカウントJSON | Firebase APIの認証情報 | APIアクセスの認証 |
8. プロビジョニングプロファイルの配置場所
Section 7で、P12もプロビジョニングプロファイルも、Base64でテキストにしてGitHub Secretsに保管→CIで復元する流れがわかりました。
復元したP12は Section 6 のキーチェーンに入れればOKです。では、復元したプロビジョニングプロファイルはどこに置けばよいのでしょう?
CIサーバーでは、プロビジョニングプロファイルを macOS の特定のフォルダ に置く必要があります。
~/Library/MobileDevice/Provisioning Profiles/
ここは「Xcodeさんは、このフォルダを見れば必ずプロファイルがあると思っている場所」です。OSの「お約束の置き場所」と思ってください。
Xcode(および前述の codesign コマンド)は、このフォルダの中からプロファイルを自動的に探します。CIワークフローでは:
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
cp decoded_profile.mobileprovision ~/Library/MobileDevice/Provisioning\ Profiles/
このフォルダにプロファイルを置くだけで、ビルド時に自動的に参照されます。「置けば見つけてくれる」のがポイントです。
9. Automatic Signing vs Manual Signing — CIでの注意点
これで、CIサーバーに必要な材料(証明書、PP、配布設定ファイル)はすべて揃って、正しい場所に配置できる状態になりました。
ところが、実はもう一つ罠があります。Xcodeのプロジェクト設定そのものが、ローカル開発用とCI用で異なるのです。普段の開発と同じ設定のままCIでビルドしようとすると、ここで止まってしまいます。
ローカル開発(Automatic Signing)
Xcode の「Automatically manage signing」にチェックを入れておくと、Xcode が Apple Developer Portal と通信して、証明書やプロファイルを自動管理してくれます。
開発者は何も考えなくていいので楽です。普段の開発では、ほとんどの人がこの方式を使っています。
CIでの問題
ところが、CIサーバーでは Apple ID にログインしていないため、Automatic Signing は動きません。
そのため、CI ワークフロー内で Xcode プロジェクトの設定を一時的に Manual に書き換える 必要があります。
具体的には、project.pbxproj というXcodeのプロジェクト設定ファイルを書き換えます。
# project.pbxproj 内の設定を書き換え
CODE_SIGN_STYLE = Automatic → CODE_SIGN_STYLE = Manual
また、署名に使う Identity(証明書名)も開発用から配布用に変更します。
"Apple Development" → "Apple Distribution"
"Apple Developer" → "Apple Distribution"
ここで重要なのは、この書き換えはCIの中だけで行われるということです。
CIは毎回まっさらな状態で起動して、リポジトリのコードを取ってきて、その場で書き換えてビルドして、終わったら全部破棄します。リポジトリのコードは変更されません。
つまり、ローカル開発は引き続き Automatic Signing のままで問題ありません。開発者は普段通り作業できます。ローカルとCIで違う設定で動く、というのが面白いところです。
10. Firebase App Distribution — テスターへの配布
Section 9までで、ようやく 「CIサーバーで iOSアプリをビルドして IPA を生成する」 ところまでたどり着けました。
最後に残った仕事は、「ビルドしたIPAを、どうやってテスターに届けるか」 です。これを担うサービスのひとつが Firebase App Distribution です。
Firebase App Distribution とは
Google の Firebase が提供するアプリ配布サービスです。
ビルドしたIPAをアップロードすると、登録されたテスターにメールで通知が届き、テスターはワンタップでアプリをインストールできます。
「テスターに配布」と聞くと「TestFlight(Apple純正のテスト配布サービス)でいいのでは?」と思うかもしれませんが、Firebase App Distribution には次のメリットがあります。
- Apple の審査を待たずに即配布できる
- iOS と Android を同じ仕組みで配布できる
- グループ管理がしやすい
CI との連携で必要なもの
| 必要なもの | 用途 |
|---|---|
| Firebase App ID | 「どのアプリにアップロードするか」の識別子。1:123456:ios:abcdef のような形式 |
| サービスアカウントJSON | Firebase の API を呼び出す認証情報。人間の代わりにCIがログインするための「ロボット用アカウント」 |
| テスターグループ | 配布先のグループ名(例: testers)。Firebase Console で事前に作成しておく |
「サービスアカウント」は、AIエージェント時代によく出てくる概念です。人間のメールアドレスとパスワードではなく、機械(プログラム)が使うための専用アカウントだと思ってください。これにより、人間がログインしなくてもCIが自動でAPIを叩けるようになります。
IPAファイルとは
序盤でも触れましたが、改めて。
IPA(iOS App Store Package)は、iOSアプリの配布用ファイル形式です。AndroidでいうAPKに相当します。
APK → Android アプリの配布ファイル
IPA → iOS アプリの配布ファイル
Flutter を使っている場合は flutter build ipa コマンドで生成されます。ネイティブ開発の場合は Xcode の「Archive → Distribute」で生成されます。
11. Entitlements — アプリの「特別な権限」
これで「ビルド→配布」の主要な流れは網羅しました。理屈の上では、ここまでの知識でCIが動くはずです。
ところが、実際にやってみると 「ローカルではビルドできるのに、CIだとなぜか落ちる」 という現象に遭遇することがあります。原因の多くは、これから説明する「Entitlements」と配布方法の相性問題です。最後の落とし穴として押さえておきましょう。
Entitlements とは
ここまで読んできて、「そもそもアプリが使える機能って、何で決まるんだろう?」と思った方もいるかもしれません。
iOS では、アプリが「Push通知を送る」「位置情報を使う」「他のアプリとデータを共有する」といった特別な機能を使うとき、事前に「この機能を使います」と宣言する必要があります。その宣言ファイルが Entitlements です。
例えるなら「特別エリア入場許可証」。動物園で「バックヤードに入っていいエリア」が決まっているのと同じで、アプリも「使っていい機能」を申告制で決めています。
<!-- Runner.entitlements -->
<dict>
<!-- App Groups: アプリとウィジェット間でデータを共有する権限 -->
<key>com.apple.security.application-groups</key>
<array>
<string>group.com.example.myapp.shared</string>
</array>
</dict>
「App Groups」は、メインアプリとウィジェットなどの拡張機能の間でデータを共有するための仕組みです。たとえば、アプリで設定した内容をウィジェットに反映したいときに使います。
CIでの注意点
ここで、また初学者がハマりがちなポイントです。
一部の Entitlements は、特定の配布方法でしか使えないものがあります。
例えば「拡張仮想アドレッシング」(メモリをたくさん使う権限)は、App Store 配布では使えても Ad Hoc 配布ではサポートされない ことがあります。
「ローカルでは普通にビルドできるのに、CIではエラーになる」という現象は、これが原因のことが多いです。CI で Ad Hoc ビルドをする場合は、こうした非対応の Entitlements を事前に除去しないとビルドが失敗します。
plutil -remove 'com.apple.developer.kernel.extended-virtual-addressing' \
ios/Runner/Runner.entitlements || true
|| true を付けるのは、「その Entitlement がなくてもエラーにしない」ためです。除去対象がそもそもなくても、CIを止めない安全装置です。
12. 全体像 — 登場人物の関係図
ここまで、「証明書 → P12 → プロビジョニングプロファイル → 配布設定 → キーチェーン → Secrets → 配置場所 → 署名方式 → 配布 → Entitlements」 という順番で、登場人物を一つずつ見てきました。
最後に、これらの登場人物がどう連携しているのかを 1枚の図で俯瞰 しておきましょう。この図が頭に入っていれば、AIが出力したワークフローも、エラーメッセージも、迷子にならずに読めるようになります。
この図を頭に入れておくと、エラーが出たとき「どの段階で何が起きているか」が瞬時にわかるようになります。
たとえば「provisioning profile not found」というエラーが出たら、真ん中下の領域(CIサーバー)の3番のステップが疑わしい、と当たりがつけられるわけです。
用語集(困ったときの辞書)
| 用語 | 説明 |
|---|---|
| コード署名 | アプリに「誰が作ったか」の電子署名をつけること。iOSでは必須 |
| Certificate(証明書) | Appleが発行する開発者の身分証明書。公開鍵を含む |
| CSR | Certificate Signing Request。Appleに証明書の発行をお願いする申請書 |
| 秘密鍵 | CSR作成時に自動生成される、本人だけが持つ鍵。署名に使う |
| P12(PKCS#12) | 証明書と秘密鍵を1つのファイルにまとめた形式。CI に持っていく時に使う |
| Provisioning Profile | App ID・証明書・デバイスを結びつけた許可証。.mobileprovision ファイル |
| Bundle ID | アプリの一意な識別子。com.example.myapp の形式 |
| App ID | Bundle ID をApple Developer Portal上で登録したもの。ほぼ同義で使われる |
| Team ID | Apple Developer Programのチーム識別子。10文字の英数字 |
| UDID | iPhone固有の識別番号。Ad Hoc配布ではこれで端末を登録する |
| Ad Hoc | 登録済みデバイス(最大100台)に直接配布する方式 |
| APK | Android アプリの配布用ファイル形式 |
| IPA | iOS App Store Package。iOSアプリの配布用ファイル(.ipa) |
| ExportOptions.plist | IPAエクスポート時の設定ファイル。配布方法・署名方式・PP名を記述 |
| Keychain(キーチェーン) | macOSの鍵管理システム。証明書や秘密鍵を暗号化して保管する |
| Base64 | バイナリデータをテキスト文字列に変換するエンコード方式 |
| GitHub Secrets | GitHubリポジトリに紐づく暗号化された変数保管庫。ログにも表示されない |
| Entitlements | アプリが使える特別な権限の宣言ファイル。App Groups、Push通知など |
| Firebase App Distribution | テスターにアプリを配布するFirebaseのサービス |
| サービスアカウント | 人間の代わりにAPIを呼び出すための「ロボット用アカウント」 |
おわりに:理解は最強の武器
ここまでお読みいただき、お疲れ様でした。
冒頭で触れたとおり、iOSのCI構築は「コピペで動かす」アプローチで挫折する人が多い領域です。理由はシンプルで、登場人物の役割を理解せずに呪文を貼り付けても、エラーが出たときに何を直せばいいかわからないから。
逆に言えば、登場人物を理解してしまえば、もう怖くありません。
AIが出力したワークフローを読んで「ここでP12をキーチェーンに入れているんだな」「ここでExportOptions.plistを生成しているな」と会話できるエンジニアになれれば、エラーが出ても「どのステップが怪しいか」を自分で考えられるようになります。
最初の構築は確かに骨が折れます。でも一度理解してしまえば、次のプロジェクトでも、別のアプリでも、応用が効きます。この記事がその一助になれば幸いです。
もし役に立ったと感じていただけたら、LGTM や ストックで応援していただけると、続編を書く励みになります。
それでは、よいiOSライフを。
























