Bazel 9 iOSビルドで完全なiOS成果物まで作るなら、Apple SDKとXcodeを備えた実機のmacOSノードを残してください。Linuxにはコード検査や一部のテストを置き、小規模チームは単一のリモートMacで閉ループを確認し、中規模以上のチームはLinuxとリモートMacの混合CIへ分けるのが安全です。

対象は、Bazel 9をiOSプロジェクトへ導入するビルドエンジニア、Linux CI基盤を管理するDevOpsエンジニア、Appleプラットフォーム用ノードの増設を判断する技術責任者です。単にコマンドが起動するかではなく、実行場所、ツールチェーン、署名、復旧性を証拠にして構成を決めます。

最終更新:2026年8月29日。Bazelのリリース情報、Bazel 9公式告知、rules_appleとrules_swiftの公開情報、AppleのXcode 26リリースノートを基に確認しています。小バージョンの組み合わせは、導入時点のリリース情報で再確認してください。

01

Bazel 9 iOSビルドの判定はツールチェーンから始める

Bazelは依存関係とビルドアクションを管理しますが、Apple SDKやXcodeそのものを提供する仕組みではありません。rules_appleはApple向けのビルドルール、rules_swiftはSwiftのビルド統合を担い、XcodeはSDK、コンパイラー、Simulator、署名関連のApple開発環境を提供します。

Bazel 9はActive LTSとして案内されていますが、採用判断は「Bazel 9なら動く」という一語では足りません。BazelのリリースモデルBazel 9の公式告知を確認し、対象のrules_apple、rules_swift、Xcode 26、macOSの組み合わせを固定してください。

特に、rules_appleの対応範囲は公式リリース情報で、rules_swiftの対応状況は公式リポジトリの説明で確認します。Xcode 26のSDKやツールチェーン要件はAppleのリリースノートを基準にします。

互換性を止める基準

次のどれかに該当する場合、Linuxだけへの移行を止め、macOSノードで検証を続けます。

  • 対象SDKをBazelのツールチェーンが解決できない。
  • rules_appleまたはrules_swiftの対応範囲が、採用するBazel 9の組み合わせと一致しない。
  • Xcode 26で必要なApple SDK、Simulator、署名ツールが実行環境にない。
  • ルールのリリースノートに未解決の互換性問題があり、公式に確認できる回避策もない。

Bazel 9で旧式のWORKSPACE依存を使っている場合は、依存関係の入口を別途確認します。Bazel 9での移行内容は公式告知に記載された変更を起点にし、MODULE.bazelへ移せば自動的に再現可能になる、と決めつけないことが重要です。

02

Linuxに残せる処理とmacOSへ送る処理を分離する

CIのジョブ名に「build」や「test」と書かれていても、それだけでは実行場所を決められません。Bazelのアクションは入力ツール、プラットフォーム、toolchainの解決結果によって実際のノードが決まるため、platformsとtoolchainsの公式リファレンスに沿って条件を宣言します。

Linuxに残しやすい処理は、たとえば次のとおりです。

  • ソースコードの整形、静的解析、ライセンス検査
  • Apple SDKを参照しないコード生成
  • 依存関係の検証とBazelグラフの検査
  • プラットフォームに依存しない単体テスト
  • 生成物の形式検査やメタデータ検査

一方、macOSへ送るべき処理は、Apple SDKを使うコンパイルとリンク、iOS Simulatorの起動、署名、Archive、配布用エクスポートです。Appleのプラットフォーム要件でも、SDKやXcodeを前提とする作業には対応したApple開発環境が必要になるため、Linux上の成功だけで出荷可能とは判定できません。Appleのプラットフォーム構築要件もリリース前に確認します。

第一歩:actionログで実行位置を確定する

まず、CIでBazelの実行ログを保存します。アクションの詳細表示を有効にし、実行プラットフォーム、使用toolchain、入力されたSDK、リモート実行の有無を追跡できる状態にします。

bazel build //App:App \
  --announce_rc \
  --verbose_failures \
  --profile=<ARTIFACT_DIR>/bazel-profile.json

出力例は次のような情報を含む形にします。実際の表示形式はBazelの設定や実行基盤によって変わるため、文字列の完全一致ではなく、実行ノードと入力条件を記録します。

INFO: Build request: //App:App
INFO: Execution platform: //platforms:macos_arm64
INFO: Toolchain: //toolchains:apple_xcode
INFO: Action cache hit: false
INFO: Spawn result: remote

Execution platformがmacOSでも、SSHで接続しただけのMac上でBazel全体を実行しているのか、リモート実行サービスがMacアクションを処理しているのかは別問題です。BazelのRemote Executionに関するルールと、リモートビルド実行の説明を読み、ログの項目を分けて保存してください。

03

再現性はバージョン表とクリーンビルドで測る

「同じコミットをビルドした」というだけでは再現性の証明になりません。MODULE.bazel、rules_apple、rules_swift、Bazel、Xcode、macOS、SDKを同じ組み合わせとして追跡できる必要があります。

PATHに偶然入ったツール、ホスト側のファイル、宣言されていないスクリプト、ユーザーのホームディレクトリにある設定ファイルは、ローカルでは見えにくい非密封依存です。CIノードの入れ替えやキャッシュ削除で初めて失敗するため、構成管理の外にある入力を洗い出します。

git clone <REPOSITORY_URL> <WORKSPACE_DIR>
cd <WORKSPACE_DIR>
bazel clean --expunge
bazel build //App:App --disk_cache=<EMPTY_CACHE_DIR>

証拠として保存するのは、コミット識別子、Bazelのバージョン、XcodeとSDKの情報、解決済みモジュール、actionログ、失敗したアクションです。クリーンなクローンと空のローカルキャッシュから同じ成果物が得られるかを確認し、差分が出た場合は「キャッシュの問題」ではなく、まず未宣言入力を疑います。

04

キャッシュ、署名、復旧を別々の指標にする

遠隔キャッシュ、遠隔実行、リモートMacへのログインは似た言葉で語られますが、役割が異なります。キャッシュは以前のアクション結果を再利用する仕組み、遠隔実行はアクションを別ノードで実行する仕組み、SSHやVNCは人やCIがMacへ接続する経路です。

キャッシュを導入しても、署名鍵やprovisioning profileの管理が自動的に安全になるわけではありません。証明書、キーチェーン、署名用の秘密情報、ビルドキャッシュは分離し、署名済み成果物を共有キャッシュへ不用意に置かない設計にします。署名の検証は、Appleのコード署名とprovisioning profileの技術資料を基準にしてください。

実行効率を数値化する記録項目

提案された構成の速度を、根拠のない倍率で比較してはいけません。次の項目を同じコミットと同じテスト条件で記録すると、ボトルネックが計算、I/O、キュー待ち、キャッシュ、ノード不足のどこにあるかを切り分けられます。

  • キャッシュ命中とミスの割合
  • Macノードのキュー待ち時間
  • コンパイル、リンク、Simulatorテスト、署名、Archiveの各段階
  • 失敗アクション数と再試行の結果
  • ノード再起動後に自動復帰するまでの状態
  • 同時実行数を増やした際の待ち時間と失敗傾向

署名なしのビルドが成功しても、App Store提出や社内配布までの経路が使えるとは限りません。Simulatorテスト、Archive、署名情報、エクスポートされた成果物を個別に検査し、無人セッションで完了することを確認します。

05

構成を決める3つの比較表

次の表では、プラットフォーム境界を先に固定します。すべての処理をMacへ移すのではなく、Apple SDKへの依存があるかを分岐条件にします。

処理 Linux候補 macOS必須候補 判定する証拠
静的解析・整形 可能 不要 実行ツールがApple SDKを参照しない
コード生成 条件付きで可能 生成ツールがXcode依存なら必要 actionの入力ツール
Swift・Objective-CのiOS向けリンク 不可 必須 Apple SDKとtoolchain
Simulatorテスト 不可 必須 Simulator起動ログ
コード署名・Archive 不可 必須 署名情報と成果物

次に、構成ごとの適用条件を比較します。小規模チームでも、出荷に必要な署名と復旧まで確認するなら、単一Macを「本番の冗長構成」と誤認せず、検証用の閉じた実行環境として扱います。

構成 適する条件 利点 注意点
単一のリモートMac iOS用の閉ループを先に確認したい Xcode、Simulator、署名を一つの環境で追跡しやすい ノード停止時に待ち行列が集中する
Linux+リモートMac 検査とApple専用処理を分離したい Linux側の汎用処理とMac側の出荷処理を分担できる platform、toolchain、秘密情報の境界設計が必要
移行を延期 対応マトリクスや署名要件が確定しない 不安定な本番切り替えを避けられる 現行環境の保守コストと移行条件を記録する必要がある

最後に、ノードの拡張条件を先に決めます。待ち時間だけを見て増設すると、実際にはキャッシュミスや署名処理が原因だった場合に、台数だけ増えて運用負荷が残ります。

観測結果 主な原因候補 次の対応
Macのキュー待ちだけが増える Apple専用ノード不足 Macノードの追加を検証する
キャッシュミス時だけ遅い 入力やツールチェーンの不一致 密封性とキャッシュキーを確認する
署名段階で失敗する キーチェーンやprofileの管理不備 署名環境を分離して再検証する
再起動後に手動ログインが必要 GUI依存またはサービス起動不備 無人復旧手順を修正する
Linux処理もMacで待つ ルーティング条件が広すぎる platform制約とジョブ分割を見直す
06

Macノード投入前の検証手順

実際のプロジェクトを使い、次の順序で検証記録を作成します。

まず互換性を固定する

MODULE.bazelとルールのリリースを確認し、Bazel 9、rules_apple、rules_swift、Xcode 26、SDK、macOSの組み合わせを一枚の記録にまとめます。公式に確認できない組み合わせは「対応済み」ではなく「要検証」と表記します。

次にアクションを分類する

最小のiOSプロジェクトで、コード生成、コンパイル、リンク、Simulatorテスト、署名、Archiveを実行します。各アクションについて、実行プラットフォーム、toolchain、キャッシュ状態、成果物を保存します。

キャッシュを空にして再実行する

ローカルキャッシュと共有キャッシュを分け、空の状態から再実行します。キャッシュヒット時だけ成功する処理や、Macホストにしか存在しないファイルを利用する処理は、本番投入前に修正します。

無人の署名と成果物を検査する

CIの非対話セッションで署名を実行し、成果物の署名情報、Archive、エクスポート形式、対象Bundleを検査します。証明書を配置しただけで成功とせず、期限切れやprofile不一致の失敗も記録します。

障害と交換を再現する

SSH切断、Mac再起動、Xcode環境の切り替え、ノード交換を順に試します。CIがグラフィカルな手動ログインを要求せず、ジョブを再試行または安全に失敗として終了できることが合格条件です。

NodeMiniのMacレンタル環境の選択肢を検証先として使う場合も、最初から長期の台数を決めるのではなく、実際のBazel iOSプロジェクトでこの記録を作成します。Mac miniを自前で用意する案と比較する場合は、Mac miniの利用方法に関する案内も確認し、物理接続が必要か、常時稼働が必要か、交換時の復旧を誰が担当するかまで比較してください。

現在のLinux専用CIは、Apple SDKとXcodeを扱えず、Simulatorや署名を別の運用へ分ける必要があり、最終的に手動のMac作業や成果物の受け渡しが残りやすい点が弱点です。自前のMac miniも、購入、保守、故障時の交換、遠隔復旧の責任をチーム側が負います。そのため、まず隔離されたリモートMacでキャッシュ、テスト、署名、再起動復旧を確認し、結果に応じてNodeMiniの利用期間や混合ノード数を決める方法が、短期のCI検証には適しています。

07

よくある確認事項

FAQでは、Xcodeの必須範囲、Linuxで可能な処理、macOSへ送るaction、混合クラスタへの接続方法、ノードの受け入れ試験を分けて確認します。本文の比較表とログ確認を実装した後に、チームの判定記録へ転記してください。

必要なMacの台数や利用期間は、Bazelのキャッシュ命中率、Macキュー待ち、署名処理、同時実行数を記録してから決めます。長期に安定した高負荷を処理し、物理USB機器や実機を常時接続する必要がある場合は、自前設備や専用構成の方が適する場合もあります。短期検証、リリース前の増強、Apple専用処理の切り分けが目的なら、リモートMacを候補にできます。