同じ Xcode プロジェクトが、長期間使用してきた開発ディレクトリではビルドできるのに、新しい VPSGit クラウド Mac ワークスペースへコピーすると、ファイルが存在しない、スクリプトが見つからない、設定値が空であるといったエラーが発生することがあります。この場合、最初に古いキャッシュを移行してはいけません。本当に検証すべきなのは、指定したコミット、明示的に定義されたツールチェーン、パイプラインから注入されたパラメータだけでビルドを完了できるかどうかです。
クリーンチェックアウトで隔離すべき状態
DerivedData を削除しても、除外できるのは一種類のキャッシュにすぎず、プロジェクトにローカル環境への依存がないことの証明にはなりません。長期間使用してきたワークスペースには通常、未コミットのファイル、ユーザーディレクトリ内の設定、グローバルにインストールされたツール、Shell 初期化スクリプト、リポジトリ外を指す絶対パスなどが積み重なっています。
少なくとも、次の5つのレイヤーを隔離する必要があります。
- 元のディレクトリをクリーンアップするのではなく、独立したクローンを使用する。
- タスクごとに一時的な
HOMEとTMPDIRを作成する。 - DerivedData にタスク専用のディレクトリを指定する。
- デフォルトの Xcode バージョンが変動しないよう、
DEVELOPER_DIRを固定する。 - ビルドプロセスには、許可リストに含まれる環境変数だけを渡す。
クリーンチェックアウト検証の目的は、ビルド前のクリーンアップ回数を増やすことではありません。バージョン管理、依存関係マニフェスト、パイプラインパラメータのいずれにも含まれていない状態が、暗黙のうちに利用されないことを証明することです。
初回の調査では、元のワークスペースではビルドに成功し、隔離したワークスペースでは失敗するという比較条件を残しておきます。両方で同じコミット、Scheme、Configuration、Destination を使用して初めて、失敗の差分が診断に役立ちます。
使い捨ての隔離ワークスペースを作成する
次のスクリプトは、既存のリポジトリからローカルハードリンクを使用しないクローンを作成し、指定したコミットをチェックアウトします。さらに、ユーザーディレクトリ、一時ディレクトリ、ビルドキャッシュをすべて使い捨てディレクトリ内に配置します。プロジェクト名と Scheme は引数で渡し、環境ごとの差異をスクリプト内に固定しないようにしています。
#!/bin/bash
set -euo pipefail
SOURCE_REPO="${1:?source repository required}"
REVISION="${2:?revision required}"
PROJECT="${3:?project path required}"
SCHEME="${4:?scheme required}"
ROOT="$(mktemp -d "${TMPDIR:-/tmp}/clean-checkout.XXXXXX")"
trap 'rm -rf "$ROOT"' EXIT
git clone --no-local --no-checkout "$SOURCE_REPO" "$ROOT/repo"
git -C "$ROOT/repo" checkout --detach "$REVISION"
git -C "$ROOT/repo" submodule update --init --recursive
mkdir -p "$ROOT/home" "$ROOT/tmp" "$ROOT/DerivedData"
export HOME="$ROOT/home"
export TMPDIR="$ROOT/tmp"
export DEVELOPER_DIR="/Applications/Xcode.app/Contents/Developer"
cd "$ROOT/repo"
xcodebuild \
-project "$PROJECT" \
-scheme "$SCHEME" \
-configuration Debug \
-derivedDataPath "$ROOT/DerivedData" \
CODE_SIGNING_ALLOWED=NO \
build | tee "$ROOT/build.log"
CODE_SIGNING_ALLOWED=NO は、署名を必要としないコンパイル工程の検証にのみ適しています。ターゲットでアーカイブや署名まで完了する必要がある場合は、CI から必要なデータを安全に注入してください。スクリプトを成功させるために、個人のユーザーディレクトリ内にあるファイルを参照してはいけません。
依存関係の不足を早期に検出する
クローン完了後、まず git status --porcelain を実行し、出力が空であることを確認します。続いて、サブモジュールの状態と大容量ファイルのオブジェクトが完全に揃っているかを確認します。プロジェクトにコード生成工程が含まれる場合は、2種類の成果物を明確に区別する必要があります。ジェネレーターのソースコードとバージョンは固定し、生成結果については、チームの方針に従ってコミットするか、ビルド中に安定して生成できるようにします。
~/bin、/usr/local/bin、または特定メンバーのデスクトップディレクトリを、問題回避のための検索パスとして追加してはいけません。補助ツールは、リポジトリ内のツールディレクトリ、バージョン制約付きのパッケージ管理マニフェスト、または固定バージョンをインストールするノード初期化プロセスのいずれかで管理します。
失敗ログから暗黙の入力を特定する
隔離ビルドが失敗したら、ログ末尾の集計ではなく、最初に発生した実質的なエラーを探します。代表的な兆候は、入力の種類ごとに次のように分類できます。
| 失敗の兆候 | よくある原因 | 修正方針 |
|---|---|---|
No such file or directory |
未コミットのファイルまたは絶対パス | ファイルをリポジトリに追加し、リポジトリ相対パスを使用する |
command not found |
対話型 Shell またはグローバルツールへの依存 | ツールのバージョンを定義し、PATH を明示的に設定する |
| 設定値が空 | パラメータがローカル設定にしか存在しない | CI から注入し、欠落時には即座に失敗させる |
| 古いディレクトリでのみモジュールが見つかる | 共有キャッシュが依存関係の不足を隠している | 古いキャッシュをコピーせず、依存関係の宣言を修正する |
| スクリプトの権限エラー | 実行権限がコミットに含まれていない | ファイルモードを修正して再コミットする |
次のコマンドを使用すると、対象ファイルとその権限が現在のコミットに実際に含まれているかを確認できます。
git ls-tree -r HEAD -- path/to/file
git status --short
git diff --summary HEAD
エラーに含まれるパスが /Users/ある名前/ を指している場合、多くはプロジェクト設定、スクリプト、または生成ファイルに絶対パスが記録されています。新しいノードに同名のディレクトリを作るのではなく、そのパスを生成した元の設定を修正してください。
誤検出を起こさずに環境を最小化する
env -i を直接使用すると環境依存をすばやく検出できますが、ビルドに必要な PATH、ロケール設定、一時ディレクトリまで削除してしまう可能性があります。より確実なのは、現在のビルドが実際に読み取っている変数を先に記録し、その後で許可リストを作成する方法です。
HOME、TMPDIR、PATH、DEVELOPER_DIR、LANG、およびパイプラインから明示的に注入される業務用パラメータは残すことを推奨します。各カスタム変数には、次のような必須チェックを設定します。
: "${API_BASE_URL:?API_BASE_URL must be provided by CI}"
: "${BUILD_FLAVOR:?BUILD_FLAVOR must be provided by CI}"
これにより、設定が不足している場合はスクリプトの開始時点で失敗し、コンパイル後半で曖昧なエラーが発生するのを防げます。機密値については存在の有無だけを確認し、ログへ出力してはいけません。また、非対話型タスクでは初期化ファイルが読み込まれない可能性があるため、ビルドスクリプトから対話型 Shell の設定を参照しないようにします。
検証を日常のパイプラインへ組み込む
完全に独立したクローンは、ディスク使用量とチェックアウト時間を増加させるため、小さなコミットのたびに実行を必須にする必要はありません。2段階に分け、通常のタスクでは再利用可能なワークスペースで迅速なフィードバックを行い、クリーンチェックアウトタスクは主要ブランチへのマージ時、依存関係や Xcode の変更時、リリース前に実行できます。
検証に合格するには、次の条件をすべて満たす必要があります。
- 指定したコミットを独立クローンへ完全にチェックアウトできる。
- ビルドの前後でワークツリーに意図しない変更がない。
- ビルドが元のワークスペースや個人のユーザーディレクトリを参照しない。
- すべての補助ツールに追跡可能なバージョンの取得元がある。
- DerivedData を削除しても期待する成果物を生成できる。
- 失敗ログと環境の概要を再現に利用でき、機密値が含まれていない。
通常のビルドは成功するのにクリーンチェックアウトが失敗する場合は、依存関係の宣言に不備があると判断すべきです。キャッシュの復元を検討する前に、まず入力の境界を修正してください。この検証を継続することで、新しいノードへの移行、並列スケールアウト、ツールチェーンのアップグレードを、「別のマシンで運任せに試す作業」から再現可能なエンジニアリングプロセスへ変えられます。
よくある質問
git cleanだけでは不十分なのはなぜですか?
git cleanが削除できるのは現在の作業ツリーにある未追跡ファイルだけです。HOMEの設定、グローバルツール、共有キャッシュ、Keychain、リポジトリ外の絶対パスは隔離できません。
クリーンビルドが失敗したら最初に何を確認しますか?
失敗したコマンドの入力パスと環境変数を比較し、サブモジュール、大容量ファイル、生成コード、設定ファイル、補助ツールがリポジトリやロック情報に宣言されているか確認します。
すべてのコミットで完全な検証が必要ですか?
必須ではありません。通常の処理には短い確認を残し、完全な隔離検証はリリース前、Xcodeや依存関係の更新後、主要ブランチへの重要なマージ時に実行します。
次のビルドや推論タスクに向けてクラウドMacを構成
3種類のApple Silicon構成、6つのノード、固定利用期間から選択できます。実際の利用可能状況はコンソールのリアルタイム表示をご確認ください。