エンジニアリングガイド

クラウドMacで大規模iOSリポジトリを効率よく取得する

クラウドMacで大規模iOSリポジトリを効率よく取得する

長年にわたるコミット履歴、デザインのソースファイル、画面収録、テストフィクスチャを含む iOS リポジトリを初めてクラウドMacへ配置すると、「マシンの準備が遅い」原因を CPU やディスクの性能だと誤認しがちです。しかし実際のボトルネックは、ビルドが始まる前に発生していることが少なくありません。通常の Git オブジェクトがすべてダウンロードされ、チェックアウト時に Git LFS が大容量ファイルを自動取得する一方、CI が最終的にビルドするのは 1 つの App だけ、というケースです。対策はディレクトリ全体を闇雲にキャッシュすることではありません。まず入力を固定し、コミットオブジェクト、ワークツリーのパス、LFS コンテンツをレイヤーごとに制御します。

チェックアウトのコストを測定する

git clone の所要時間だけを記録してはいけません。少なくともネットワーク転送、ワークツリーの展開、LFS のダウンロードという 3 つの段階に分け、以下の結果を保存します。

git --version
git lfs version
du -sh .git
du -sh .git/lfs 2>/dev/null || true
du -sh .
git count-objects -vH
git lfs ls-files | wc -l

初回実行とワークツリー再利用時は別々に測定します。初回実行ではリモートからの転送コストを確認でき、再利用時の測定からは、不要なクリーンアップ処理、履歴オブジェクトの増加、LFS キャッシュの肥大化が見えてきます。また、ブランチ名だけでなく、最終的に使用したコミット値も記録してください。

git rev-parse HEAD
git status --short

最適化の目的は、ディレクトリを見かけ上小さくすることではありません。同じコミット、同じディレクトリ集合、同じ LFS オブジェクト群を安定して再構築できるようにすることです。

部分クローンとスパースチェックアウトを組み合わせる

部分クローンの blob:none は通常ファイルの内容取得を遅延させ、スパースチェックアウトはワークツリーへ実際に展開するディレクトリを制限します。この 2 つは併用できますが、Git LFS の取得範囲まで自動的に制限されるわけではありません。そのため、最初の smudge を明示的にスキップします。

export GIT_LFS_SKIP_SMUDGE=1
git clone --filter=blob:none --no-checkout "$REPO_URL" app
cd app
git lfs install --local
git sparse-checkout init --cone
git sparse-checkout set App Packages Shared
git fetch --depth=1 origin "$BUILD_REF"
git checkout --detach FETCH_HEAD
git rev-parse HEAD

--cone モードはディレクトリ単位で構成されたプロジェクトに適しています。ルールが単純で、親ディレクトリのファイルが意図せず消える問題も起きにくくなります。AppPackagesShared は、実際のビルド依存関係に合わせて置き換える必要があります。ワークスペース、スクリプト、設定ファイルがリポジトリのルートにある場合は、それらが引き続き含まれていることを確認してください。

部分クローンを使用するには、リモート側がオブジェクトフィルタリングに対応している必要があります。コマンド実行時にフィルタリング機能に関する警告が表示された場合は、通常のクローンへフォールバックしたものとして扱い、.git のサイズを改めて記録してください。最適化が有効になったと決めつけてはいけません。

現在のタスクに必要な LFS オブジェクトだけを取得する

チェックアウトが完了しても、LFS ファイルはポインタのまま残っている場合があります。まず現在のコミットが参照しているオブジェクトを一覧表示し、タスクで必要なパスに限定して取得します。

git lfs ls-files
git lfs pull --include="App/Assets/**,Shared/Fixtures/**"
git lfs fsck

パスフィルタは、ファイル拡張子から推測するのではなく、ビルド入力に基づいて指定します。UI テストは画像、動画、ローカライズ用フィクスチャに依存していることがあります。ソースコードのディレクトリだけを取得すると、コンパイルには成功しても、テストが実行時に失敗する可能性があります。

確認項目 合格条件 よくある問題
git lfs ls-files 現在のコミットで LFS 管理されている項目を確認できる .gitattributes が対象コミットとともに取得されていない
ファイルヘッダーの確認 ファイルが実際のバイナリコンテンツになっている oid sha256 を含むポインタのままになっている
git lfs fsck ローカルオブジェクトの検証に成功する ダウンロードの中断、またはオブジェクトディレクトリの破損
git status --short ワークツリーに意図しない変更がない チェックアウト後にツールがリソースファイルを書き換えている

パス制約のない共通初期化スクリプトに git lfs pull を入れてはいけません。そうすると、すべてのタスクが自身に不要な素材までダウンロードすることになります。各パイプラインが必要な LFS 入力集合を宣言する方式のほうが確実です。

CI の並行実行と認証情報を扱う

各 CI タスクには独立したワークツリーを用意します。複数のタスクが同じ書き込み可能なリポジトリを共有すると、スパースルール、インデックスロック、LFS の一時ファイルが互いに干渉する可能性があります。読み取るコードが同一であっても、2 つのタスクから同時に .git/info/sparse-checkout を変更してはいけません。

認証情報は取得処理の間だけ注入し、対象リポジトリに必要な権限だけを付与します。git fetchgit lfs pull の完了後は、一時的な環境変数と認証情報ヘルパーの設定を削除してください。ログにはトークンを含むリモート URL を出力しないでください。次のコマンドで、表示される値が安全かどうかを確認できます。

git remote -v
git config --local --get-regexp 'credential|lfs' || true

サブモジュールは個別に処理する必要があります。メインリポジトリの部分クローン設定やスパースルールは、サブモジュールには自動で引き継がれません。サブモジュールでも LFS を使用している場合は、そのディレクトリ内でローカル LFS 設定をインストールし、必要な取得処理を実行します。

安全でないクリーンアップを避ける

git lfs prune は占有リポジトリには適していますが、複数のタスクで LFS オブジェクトディレクトリを共有する環境には不向きです。共有環境では、タスクごとに独立したディレクトリを作成し、終了後にディレクトリ全体を削除するほうが安全です。ワークツリーを再利用しなければならない場合は、並行実行中のタスクがないことを確認してから、クリーンアップと整合性チェックを実行してください。

ビルド後の推測ではなく事前検証を行う

実際に xcodebuild を開始する前に、軽量な検証ゲートを追加します。少なくとも、コミット値が正しいこと、ワークツリーがクリーンであること、重要なプロジェクトファイルが存在すること、LFS ポインタが実ファイルに置き換えられていることを確認し、ディスク使用量を出力します。たとえば、次のようなチェックを実装できます。

test -f App/App.xcodeproj/project.pbxproj
test -s App/Assets/LaunchVideo.mov
if grep -q "oid sha256:" App/Assets/LaunchVideo.mov; then
  exit 1
fi
git diff --exit-code
git lfs fsck
du -sh . .git .git/lfs

最後に、コミットハッシュ、スパース対象ディレクトリの一覧、LFS include ルール、検証結果という 4 項目を保存します。これにより、クラウドMac上のタスクが失敗したとき、まず入力が完全かどうかを判断してから、コンパイラ、署名、テストのログを調査できます。リポジトリ準備の不備を、ランダムなビルド障害として扱うことも避けられます。

よくある質問

部分クローンとスパースチェックアウトでGit LFSは不要になりますか?

不要にはなりません。部分クローンは通常のGitオブジェクト、スパースチェックアウトは作業ツリーのパス、Git LFSは追跡対象の大容量ファイルをそれぞれ管理します。

チェックアウト後もLFSポインタだけが表示されるのはなぜですか?

smudgeを無効にしたままgit lfs pullを実行していない可能性があります。対象パス、認証情報、現在のコミットを確認してから必要なLFSオブジェクトを取得します。

CI終了時にgit lfs pruneを実行しても安全ですか?

ジョブ専用の独立したリポジトリなら実行できます。複数ジョブでオブジェクト領域を共有している場合は削除競合を避けるため、ワークスペース単位で破棄してください。

専用クラウドMac

専用物理ノードでビルドタスクを実行

MiniRents M4またはMiniRents M4 Proを日単位、週単位、月単位、四半期単位から選択し、チームとコードリポジトリの場所に合わせてノードを選べます。物理マシンを専有利用でき、仮想マシンではありません。

モデルを選んで注文