オンコール中で、2 台以上のレンタルクラウド Mac 上の Fastlane で iOS をリリースしており、「ビルドログは緑なのに TestFlight に新しいビルドが無い」「アップロードしたパッケージが違う」といった事象が出ている方が対象です。クラウド導入や 2 台目の要否は TestFlight 専用 Mac または Xcode ビルドのクラウド移行 を先に — 本稿はすでに1 台がビルド、1 台がアップロードに分かれている前提です。
読み終えたら、次の 2 点が言えるはずです。① ログから、証明書・成果物・ネットワークのどれが疑わしいか。② 2 台の役割分担が正しいか、マシンを増やすべきか。
- ビルド機:Xcode でプロジェクトをコンパイルし、インストールパッケージ(
.ipa)をエクスポートするだけ - アップロード機:リリース証明書で署名し
.ipaを TestFlight に送る。App Store Connect に登録する出口 IP はこの 1 台に合わせる - Archive / ビルド成果:Xcode の配布用出力。機間では完成した
.ipaだけを渡し、プロジェクトキャッシュ一式は移さない - match:Fastlane の証明書・プロビジョニング統合ツール — 1 台でのみ実行
読まなくてよい場合: Mac が 1 台だけ、または TestFlight アップロードが安定しており、まだ「ビルド + アップロード」の 2 台構成にしていない。複数のクラウド Mac でリリースしておりパイプラインに問題がある場合は、続きを読んでください。
- 証明書の境界 — リリース証明書がビルド機に同期され、アップロード機が署名できない(最も危険)
- 成果物の境界 — 検証可能な
.ipaではなく.xcarchiveを DC 間で送っている(最も見つけにくい) - ネットワーク ID のドリフト — ASC の IP 許可リストが古い IPv4 のまま(調査が最も難しい)
背景(1 分):2 台にしたあと壊れるのは境界で、CPU ではない
多くのチームは 2 台のクラウド Mac を使います。アジアに近い 1 台が昼間に App をインストールパッケージにコンパイルし、カナダの 1 台がApple にパッケージを渡す(TestFlight / App Store)。2 台目は速度のためであることが多いですが、オンコールで時間を取るのは両方が証明書とファイル転送に手を出していること — ビルドは成功表示なのに TestFlight に新版が無い、というパターンです。
まずよくある誤った分け方(モデル B)、次に当社の分け方(モデル A)と検証方法を述べます。
推奨:モデル A(ビルドはパッケージのみ、アップロードは公開のみ)
要約:ビルド機は .ipa だけを出す。アップロード機だけが証明書と TestFlight を担当する。 両方で match を走らせない — 秘密鍵が誤ったホストに残り、アップロード機で配布証明書が見つからなくなります。
| Role | ビルド機(香港) 例:APAC 昼間のビルド | アップロード機(カナダ) 固定 IP で Apple へ |
|---|---|---|
| Do | コード取得 → Xcode ビルド → .ipa エクスポート | .ipa 受領 → リリース署名 → TestFlight へ |
| Do not | match 禁止・TestFlight アップロード禁止 | |
| Hand off | Package file + build ID (RUN_ID) | None (endpoint is App Store Connect) |
当社の 2 台は Hashvps の独立 Mac mini 上で動かしています。選定理由は固定 IP とロール分離(アップロード機 IP のみ ASC に記載し、ビルド機は不要)です。香港ノードのスペックは 香港 Mac mini レンタル を参照してください。
当社の 2 台は Hashvps の独立 Mac mini 上で動かしています。選定理由は固定 IP とロール分離(アップロード機 IP のみ ASC に記載し、ビルド機は不要)です。香港ノードのスペックは 香港 Mac mini レンタル を参照してください。
よくある誤り:モデル B(両方が「手伝って」いる)
モデル B は役割が分かれていない状態です。当社も 1 週目はこう運用しました。見た目は 2 台分担ですが、実際は:
- 両方で
match→ リリース用秘密鍵がビルド機に残る → アップロード機で署名不可 → TestFlight が失敗し続ける .xcarchive一式を他ホストへコピーして再エクスポート → 設定不一致 → Apple がパッケージを拒否- 両ホストでそれぞれ
.ipaをエクスポート → オプション不一致 → ログは成功、TestFlight に使えるビルドが無い
既存パイプラインのどれかに当てはまるなら、解決策は「Mac をもう 1 台」ではなく、build / upload の role split(モデル A)が必須です。
可観測性:build と upload が同一ビルドかどうか
最も多い運用事故は「昨夜の IPA を今日アップロードした」ことです。ビルド機・アップロード機の両ログに同じフィールドを必ず出します:
RUN_ID(パイプライン 1 回分の ID。例hk-build-88421)GIT_SHA(短い commit hash)- 成果物パス + SHA256(
build/manifest.jsonに記録)
# 香港 · build · build [15:11:42]: Successfully exported: ./build/MyApp-20260604-1502.ipa [15:11:42]: RUN_ID=hk-build-88421 GIT_SHA=a3f91c2 SHA256=9f2a… # カナダ · upload · upload [02:22:01]: RUN_ID=hk-build-88421 GIT_SHA=a3f91c2 ipa_sha256=9f2a… [02:24:18]: Successfully uploaded package to App Store Connect
オンコールの鉄則:アップロード機ログに同じ RUN_ID が無ければ、成果物ミスマッチとして扱う。先に ASC を疑わない。
30 秒トリアージ:TestFlight に新しい build が無い?
まず全ログは開かないでください。下の木を 1 段だけ辿れば、該当セクションに着けます(Runbook にブックマーク推奨)。
TestFlight に新 build が無い?
├─ アップロード機 RUN_ID ≠ ビルド機 RUN_ID(または manifest 欠落)
│ → 坑 2 · 成果物境界
├─ ログに match / AppStoreDistribution / code signing identity
│ → 坑 1 · 証明書境界
└─ upload 成功表示だが ASC に無い / 403 / Invalid Binary(ビルド機は全緑)
→ 坑 3 · ネットワーク ID ドリフト
| モード | ログキーワード(grep) | Release への影響 |
|---|---|---|
| 坑 1 証明書境界 | AppStoreDistribution、No matching provisioning、match failed |
ブロッキング:TestFlight / ストア提出が全面停止 |
| 坑 2 成果物境界 | RUN_ID 不一致、Invalid Binary、export_method 衝突 |
高:誤パッケージ・昇格不可の可能性 |
| 坑 3 ID ドリフト | 403、upload OK だが processing 無し、ASC ネットワーク制限 |
間欠ブロッキング:「謎の欠落」に見える |
3 つの失敗モード(詳細)
坑 1:証明書境界エラー
30 秒判別:ビルド機の archive は緑、アップロード機が赤で、match / signing ステップで落ちている。
ログキーワード:AppStoreDistribution、Could not find a matching code signing identity、No matching provisioning profiles。
Release 影響:ブロッキング級 — 秘密鍵がアップロード機に戻るまで TestFlight / App Store アップロードは続行不可。
現象:ビルド機 archive 緑、アップロード upload 赤。TestFlight に新 build 無し。
Could not find a matching code signing identity for type 'AppStoreDistribution' ❌ Lane upload failed
根因:モデル B — ビルド機がまだ match(appstore) を実行し、Distribution 秘密鍵がアップロード機に無い。
修復:ビルド機からすべての match を削除。アップロード機だけが match を実行(rotate はアップロード機のみ・独立メンテ窓)。match readonly を参照。
坑 2:成果物境界エラー
30 秒判別:ビルド機 / アップロード機ログの RUN_ID と ipa_sha256 を突合。または ASC が Invalid Binary なのにビルド機に signing エラーが無い。
ログキーワード:RUN_ID 不一致、Invalid Binary、両機で 2 回 export_ipa / ExportOptions。
Release 影響:高 — 古いパッケージ・誤 commit のアップロード、processing 停止。パイプラインは即赤にならないこともある。
根因:DC 間で .xcarchive を送っている、または両機で export している。DC 間の最小単位は SHA 照合可能な IPA であり、archive ディレクトリではない。
修復:ビルド機で 1 回だけ export_ipa + manifest.json。アップロード機は upload のみ、再 export しない。
坑 3:ネットワーク ID ドリフト
30 秒判別:Fastlane は upload 成功。ビルド・アップロードとも signing は緑。TestFlight にパッケージが無い、または ASC 403。
ログキーワード:403、Successfully uploaded だが build 無し、ASC のネットワーク拒否(ビルド機ログは正常)。
Release 影響:間欠ブロッキング — 時々通り時々落ち、オンコールの忍耐を最も消耗させる。
根因:アップロード機の IPv4 と ASC IP 許可リストが一致しない(第 3 類の潜在故障)。
修復:アップロード機で毎日 curl -4 ifconfig.me を ASC と照合。ドリフトしたら許可リスト更新 + アラート。
本当に切り分けできているかの判断
ゲート、FAQ、オンコール項目を下の検収表に統合しました。分割から 1 週間後、各行が自動的に「はい」になるべきです。
| 検収項目 | 期待結果 |
|---|---|
| RUN_ID 整合 | ビルド機 export とアップロード機 upload のログで RUN_ID、GIT_SHA、ipa_sha256 が一致 |
| ビルド機に Distribution 無し | ビルド機のキーチェーン / ログに AppStoreDistribution、match ステップが一切出ない |
| アップロード機は upload のみ | アップロード機 lane のログに match(または resign)+ upload_to_testflight のみ。ARCHIVE SUCCEEDED は無い |
| 成果物の形 | DC 間ファイルは 200MB 未満の .ipa + manifest。.xcarchive の rsync は無い |
| ASC ID | アップロード機の現在 IPv4 = ASC 許可リスト。ビルド機 IP は ASC に未登録 |
| Runner 分離 | role=ios-build-hk と role=ios-upload-ca ラベル分離。workflow は needs で直列 |
成熟度:いまどの段階か
障害対応は「今夜どう消すか」、段階判断は「来週アーキテクチャを変えるべきか」を決めます。下の 4 段階で、維持・修正・role split のどれが適切かを見てください。
Stage 1:単一 Mac の Fastlane
特徴:Mac runner 1 台。match + build + upload が同一 Fastfile・同一キーチェーン。先にディスク、Derived Data、GitHub Actions の課金ウィンドウを最適化し、その後 build ノード追加を検討。
結論:デュアル Mac は不要。APAC のビルドが遅いだけなら、先にbuild ノードを足す。upload の分割は後回しでよい。
Stage 2:2 台あるが role split 未実施(モデル B)
特徴:Mac が 2 台以上。複数台で match / export / upload を実行。ログに本稿の 3 境界エラーが頻出。
結論:❌ 3 台目を足しても解決しない。証明書ドリフトと成果物ミスマッチだけが増える。次は runner 数ではなくモデル A に収束すること。
Stage 3:Role split(モデル A · 本稿のゴール)
特徴:build / upload 責務分離。DC 間は RUN_ID 付き IPA のみ。match は upload ノードのみ。アップロード出口 IP 固定。
結論:✔ 検収表が安定して埋まる → build runner の横展開(APAC で並列ビルド)または upload 専用機の強化が可能。固定 IP・ノード別契約は Hashvps プランを参照(先に upload 機の ASC IP を固定し、その後 build を拡張)。
Stage 4:Multi upload node(上級)
特徴:upload リージョン複数(US / EU / APAC など)。ASC API Key と IP 戦略をリージョン別。成果物昇格とコンプライアンス監査が独立ライン。
結論:配布システムの領域。「CI 用 Mac をもう 1 台」ではない — 成果物リポジトリ・ポリシー・監査が必要で、本 Runbook の範囲外。
| 段階 | 今すぐやること |
|---|---|
| Stage 1 | 単一 Mac を維持。ビルド・アップロード時間帯を最適化 |
| Stage 2 | 増設を止める。モデル A + 検収表の完走 |
| Stage 3 | build を横展開。upload 専用機と監視を強化 |
| Stage 4 | 別プロジェクト:多リージョン配布とコンプライアンス(Fastlane パッチではない) |
最終結論(この節だけで実行可能)
次の 3 条件を同時に満たすなら、本節で判断し、全文の再読は不要です:
- すでに Mac runner が 2 台以上(または同等のマルチノード CI)
- Fastlane で TestFlight 不安定 / アップロード失敗(
match failed、processing 停止、たまにパッケージ無し) matchが複数マシンで実行された可能性がある
👉 これ以上マシンを足さない。
👉 今日からできる 3 つ:
- 停止:upload 以外のノードの
match/sync_code_signing(署名権は upload 機のみ) - 強制:DC 間は
.ipa+manifest.jsonのみ(.xcarchiveの rsync 禁止) - 統一:
RUN_ID+GIT_SHA+ipa_sha256。アップロード前に必ず検証
上記 3 つができない場合:まだ Stage 2(モデル B)。増設は故障の増幅であり修復ではない。 先に Fastfile / workflow ラベルを直し、検収表を通してから build ノード拡張を議論する。
lane :upload_only do |options| verify_run_id!(options) # manifest.json match(type: "appstore", readonly: true) upload_to_testflight(ipa: options[:ipa_path], api_key_path: "asc_api_key.json") end
よく聞かれること
ビルド機で一切署名しなくて archive できる? プロジェクト設定次第です。当社はビルド機 export でアップロード機が最終 app-store 署名する IPAを渡す(またはビルド機はコンパイル検証のみで、アップロード機で resign)。要点は match が 1 台だけであること。
match の有効期限が近いときは? アップロード機のみ、非 readonly のメンテ窓で rotate。profile が安定するまでビルド機は停止。
出張向け Runbook との違いは? 出張向けは現地 Wi‑Fi と人の操作。本稿は DC ロールと Fastlane 境界。検収表はそのまま流用可。
シリーズ:本稿は ① Runbook:デュアルクラウド Mac + Fastlane 障害対応(TestFlight 未アップロード / match failed / マルチ Mac iOS CI)。続編候補:② Build·Sign·Distribute 設計、③ モデル B 反パターン。選定は TestFlight 専用 Mac。