VPSSpark ブログ
← 開発日記へ戻る

Phone 18 前にやり直した iOS CI/CD:Xcode 27、iOS 27、GitHub Actions、Apple Silicon Runner の実害記録

サーバーメモ · 2026.09.11 · 約 12 分

よくある検索:Xcode 27 · iOS 27 · xcode-27 · Apple Silicon · Phone 18 CI

ノートPCでターミナルログを確認し、ケーブル接続のスマートフォンが手元にある開発シーン
マーケ名の Phone 18 より先に、Runner イメージと simctl の実在デバイスを確認する。

Phone 18 発表会の前週、会議室でカメラの焦点距離の話をする人はいなかった——みんな Actions 一覧の赤ランプを見つめていた。Xcode 27、iOS 27 SDK、GitHub ホストの xcode-27 ラベル、そして接続したばかりの Apple Silicon セルフホスト Runner:四本の線が同じ週に重なり、どれか一本でもずれると App Store スクショと TestFlight 外測がまとめて止まる。私たちは iOS CI/CD 全体を「緑になればいい」から「リリース週を耐えられる」まで引き直し、実戦で踏んだ落とし穴をここに残す。

時間軸も硬い。2026年9月10日、GitHub は Changelog で Xcode 27 runner イメージが macOS 27 上で動くようになった と明記した。ラベルは依然 xcode-27 / xcode-27-xlarge で、arm64 のみ。前日まで macOS 26 ベースで「たまたま緑」だった job が、イメージ切替直後に OS 版、シミュレータ機種名、ディスク残量で一気に落ちることがある。Golden Gate 正式版の窓と対応機種は、先にサイト内の macOS 27 Golden Gate リリース日と対応機種 を参照してほしい。本稿は CI 側の踏み方と収め方だけを書く。

9.10
GitHub が xcode-27 を macOS 27 へ切替と発表
arm64
ホスト xcode-27 は Apple Silicon のみ
4 穴
ラベル · シミュレータ · キャッシュ · ディスク/待ち行列

なぜリリース前に「パイプライン全体を引き直す」のか——matrix 一行変更では足りない

多くのチームの筋肉記憶はこうだ:xcode-version を 26 から 27 に変え、destination を iPhone 18 に差し替える。Phone 18 ウィンドウ前にこれをやるとほぼ確実に爆発する。理由は三層ある。第一に、SDK と Runner イメージは同期出荷ではない——手元に Xcode 27 GM があっても、ホストイメージはまだ public preview か、macOS 26 ベースから 27 へ切り替わった直後かもしれない。第二に、シミュレータの機種表は変わる:iOS 27 runtime にハードコードした機種名がすぐ出るとは限らず、name=iPhone 18 を書くと即 Unable to find a device matching the provided destination になる。第三に、DerivedData・SwiftPM・CocoaPods の cache key が「Xcode メジャー版より前」のパスに紐づいたままだと、古い成果物を今日のコンパイラに食わせることになる。

私たちのやり方は「ビルドできる」と「リリース週を耐えられる」を分けて検収する。前者は単一 PR job が緑かどうか;後者は同時実行、ディスク、証明書、Simulator 冷起動が同じピークで互いの足を踏まないかだ。ホスト側はまず GitHub-hosted runners の説明 を読み、セルフホスト側はラベルで PR の速いフィードバックと Archive の重いジョブをプール分割する——以前書いた GitHub Actions が iOS / Xcode CI を遅くする新しい解法 と同じ意思決定ロジックで、今回のトリガーが Phone 18 になっただけだ。

Phone 18 前の iOS CI 四本の落とし穴:ラベル、シミュレータ、キャッシュ、ディスクと待ち行列
四本の線はこの順で並べる:まず runs-on とイメージを固定し、次にシミュレータを動的選択、次に古いキャッシュを無効化し、最後にセルフホストプールとディスク残量を語る。

落とし穴一:まだ macos-latest を使う、あるいは xcode-27 ラベルは「どの Mac でもよい」と思い込む

macos-latest は 2026年秋でも「とりあえず動く」用途には向くが、Phone 18 リリース週の唯一の検収面としては不向きだ。それは GitHub の現時点のデフォルトイメージ方針を指すだけで、「iOS 27 SDK + 必要な Simulator が揃っている」ことと同義ではない。Xcode 27 を釘付けするなら、ワークフローに明示的に runs-on: xcode-27(重いジョブは xcode-27-xlarge)と書くべきで、maxim-lobanov/setup-xcode にベースの合わないイメージ上でバージョンを無理やり切らせてはいけない——runtime が欠ける底では、Xcode.app を選んでも simctl は救えない。

より見えにくい点:Changelog は当該イメージがarm64 のみだと強調している。組織にまだ Intel セルフホストノードが残っていたり、runs-on: [self-hosted, macOS] を「すべての Mac が Xcode 27 job を受けられる」と書いていたりすると、matrix を広げた瞬間に「たまに緑・たまに赤」が起きる——緑は Apple Silicon に当たったケース、赤は Intel ノードが対応 toolchain を入れられない/回せないケースだ。ラベルは本当に用意した集合だけ書く、例:[self-hosted, macOS, ARM64, xcode27]。未アップグレードノードはそのラベルから外す。

ラベルのドリフトはコードのドリフトより危険
リリース週で一番怖いのは単発の compile error ではなく、Runner ラベルの意味が変わったのにドキュメントが更新されていないことだ。誰が xcode-27 を受け、誰が macos-26 互換線を受けるかを Runbook に書き、Actions の Runner ページで週次で突き合わせる。

落とし穴二:destination に iPhone 18 をハードコードしたが、イメージにその「電話」がまだない

マーケ文言の Phone 18 と、Simulator 一覧のデバイス名は別表だ。GitHub の actions/runner-images の説明では、iOS 27.0 runtime に現時点で並ぶのは iPhone 17 シリーズ、iPhone Air、複数の iPad など——発表会当日に iPhone 18 という名前のシミュレータが必ず出る保証はない。YAML を -destination 'platform=iOS Simulator,name=iPhone 18,OS=27.0' と書くのは、リリース前に最もよくある自傷行為だ。

再現可能なやり方:job 冒頭で xcrun simctl list devices available -j の JSON をパースし、「iOS 27 runtime + 名前が iPhone で始まる」でソートして一台取り、UDID を $GITHUB_OUTPUT に書き、以降の xcodebuild test は id=… だけ使う。多機種 matrix が必要なら「利用可能リスト ∩ チームのホワイトリスト」の積集合を取り、欠けたら skip して警告し、Release 全体を赤にしない。UI テストではもう一つ現実を受け入れる:新 OS の初回冷起動は Simulator が数分余分に食うことがある。timeout を Xcode 26 時代の 20 分のままで切ると、偶発が必発になる。

書き方リリース週リスクより安定な代替
name=iPhone 18,OS=27.0機種名未登録ですぐ失敗UDID で動的選択
OS=27.0 をマイナーまで固定イメージが 27.0.1 の可能性runtime 接頭辞一致 or 最新利用可能
macos-latest だけSDK/シミュレータが不定明示的な xcode-27 検収面
Intel + Apple Silicon 混在ラベル同一 job で偶発アーキ失敗プール分割、27 線は ARM64 のみ

落とし穴三:キャッシュ key を Xcode 27 に合わせて無効化していない——緑なのは「旧世界」

メジャーアップグレードで一番「直ったつもり」になりやすいのがキャッシュだ。SwiftPM の SourcePackages、CocoaPods の Pods、自前の DerivedData tarball で、key に branch 名や lockfile hash だけで $(xcodebuild -version)/イメージ版が入っていないと、Actions は喜んで Xcode 26 時代の成果物を restore する。症状はふわふわする:リンクで記号が見つからない、Swift driver が即死、テストは通るがモジュールキャッシュ検証が落ちる、など。

私たちは cache key に三段を強制する:Xcode 短バージョン、runner image version(ホストなら環境またはドキュメントで固定)、そして Package.resolved / Podfile.lock。切替当日は古い key 接頭辞を丸ごと変え、冷起動が 8〜12 分増えても、リリース週に「同じ commit がローカルは通って CI は落ちる」を調べない。セルフホスト Apple Silicon ノードはさらに注意:ディスク上の DerivedData をジョブ横断で共有し、Xcode 版ごとにディレクトリを分けていないと、プレビューと GM の中間ファイルが混ざる。

検収の物差し
「キャッシュヒット率の上昇」はアップグレード成功の指標ではない。「冷キャッシュでも安定して緑」こそが指標だ。リリース前に少なくとも一度、強制 miss の nightly を残す。

落とし穴四:Apple Silicon Runner は用意したのに、ディスクと待ち行列で原点に戻る

Xcode 27 + iOS 27 runtime + 複数 Simulator はディスクの殺し屋だ。ホスト xcode-27 イメージ自体がすでに重い。セルフホスト Mac mini に Xcode 26、二つの beta、三つの App の DerivedData が残っていると、Archive 後半でようやく No space left on device が出る。リリース週前、各ノードに硬ルールを置いた:現行検収の Xcode は一本だけ、Simulator runtime はホワイトリスト化、DerivedData は job 単位で掃除、毎晩 cron で df -h と xcrun simctl delete unavailable を報告。

待ち行列の問題は Silicon に換えただけでは消えない。全社が発表会前に matrix を「一世代 SDK」から「26 互換 + 27 検収」に広げると、ホスト同時実行とセルフホスト枠が同時に埋まる。PR の速いフィードバックと TestFlight Archive はラベルを分ける:前者は分単位、後者は待ちを許すが前者を塞いではならない。ピークで枠を奪うのはラベル設計であり、マシンのポスターに書かれた M シリーズ型番ではない。

公式ツールチェーンの挙動は Apple Xcode ドキュメント が正。イメージにどの Simulator が入っているかは、その週の runner-images リポジトリ Readme が正——昨年のブログのスクショを設定ソースにしてはいけない。

リリース週 Runbook:実際の切り替え手順

一日目、本番 PR 線の検収 job を明示的に xcode-27 へ切り、互換哨兵として macos-26(またはまだ支える前世代)を一本残す。失敗方針は「27 が赤ならマージ遮断、26 が赤なら警告のみ」。二日目、すべての test destination を動的 UDID に変え、YAML から Phone マーケ名を削除。三日目、キャッシュ接頭辞を無効化し、冷ビルドを強制一回走り、wall time をリリース週のベースラインにする。四日目、セルフホストプールをラベルで分割:ci-pr と ci-release。Intel ノードが誤って 27 ラベルを貼っていないことを確認。

オンコール期間の毎日三点セット:Changelog / runner-images がまたイメージを推したか、Runner のディスク残量、queued と run の比が「黄バーが緑バーより長い」状態に戻っていないか。数値が悪化したら、まず matrix 並列度を削り、そのあとマシン追加を語る——ラベルを分けずにマシンだけ増やすと、誤った job がより速く走るだけだ。

クラウド Mac / 専用 Silicon Runner をいつ入れるか
ホスト xcode-27 の待ちがビルドより長い、あるいは特定 GM イメージを釘付けして公証とアップロードしたいとき、日単位で Apple Silicon クラウド Mac をセルフホストする方が、共有キューで運を賭けるより安いことが多い。専用ノードは Release と証明書チェーン向き;大量 PR 同時実行の第一関門は、ホストのプレビューイメージに残してよい。

FAQ

今すぐすべての job を xcode-27 に移すべき?

いいえ。リリース検収とストア構築は 27 に釘付け;旧 OS 互換とホットフィックスは当面前世代イメージに残してよい。肝心なのはラベルとブランチ保護ルールを明確にし、「たまたま緑になった一台でマージ」を防ぐこと。

手元の Xcode 27 は通るのに、なぜ CI はまだ赤?

優先確認は三点:Runner が本当に arm64 の xcode-27 イメージか、シミュレータ機種名が存在するか、キャッシュが古い DerivedData を hit していないか。手元にフル runtime があっても、ホストイメージにあるとは限らない。

セルフホストと GitHub ホストを同一 workflow に混ぜられる?

できる。ただし別 job・別ラベルにする。同じ runs-on 配列がホストと任意の self-hosted の両方にマッチすると、スケジュール結果が再現できない。

Phone 18 実機テストは Simulator CI の代わりになる?

ならない。実機はカメラ、セルラー、性能体感;CI は毎回の PR のビルド、単体テスト、回帰。リリース週は両方必要で、実機だけをマージゲートの唯一条件にしてはいけない。

Phone 18 ウィンドウ · Xcode 27 オンコール

共有キューに縛られない Apple Silicon が必要ですか?

Release と公証を専用クラウド Mac に釘付けし、PR はホスト xcode-27 のまま。日単位でイメージと証明書チェーンを検証する方が、発表会当日に同時実行を奪い合うより余裕がある。

クラウド Mac プランを見る →

期間限定

共有キューに縛られない Apple Silicon が必要?

Xcode 27 · iOS 27 · 専用クラウド Mac · 日/月

ホームへ
期間限定 プランを見る