アイコンスロットが1つ欠けているだけでも、完全なアーカイブ処理が終わる直前まで問題が表面化しないことがあります。クラウドMac上で動くiOSパイプラインでは、その時点ですでに依存関係の解決、ソースコードのコンパイル、テストに時間を費やしており、最終的には Assets.xcassets 内のメタデータエラー1つで失敗することになります。より効率的なのは、Xcodeが内部で呼び出す actool を独立した事前検査として実行し、アセットの問題をパイプラインの早い段階で検出する方法です。
actoolの事前検査で検出できる範囲を明確にする
actool はAsset Catalogを読み込み、プラットフォーム、デプロイターゲット、デバイス種別に応じてリソースをコンパイルします。単独で実行すると、無効な Contents.json、画像スロットに対応するファイルの欠落、認識できない属性、名前の重複、AppIconと対象プラットフォームの不一致などを検出できます。
ただし、完全なビルドの代わりにはなりません。ソースコードが存在しないリソース名を参照している場合、targetごとに異なるAsset Catalogを使用している場合、ビルド設定によってAppIcon名が上書きされている場合などは、xcodebuild の段階で初めて問題が現れる可能性があります。適切なパイプラインの順序は次のとおりです。
| 段階 | 検査内容 | 失敗時のコスト |
|---|---|---|
| JSON構文検査 | Contents.json を解析できるか |
最低 |
| actool事前検査 | リソース構造とプラットフォーム設定 | 低い |
| Xcodeビルド | target、ソースコードからの参照、リンク | 高い |
| テストとアーカイブ | 実行時の動作と成果物 | 最高 |
事前検査の目的はXcode全体を再現することではありません。結果が明確で、修正箇所を特定しやすいアセットエラーを早期に阻止することです。
ツールチェーンと入力パラメータを固定する
最初に、現在のジョブが実際に使用しているXcodeを確認します。対話型ターミナルとCIプロセスが同じDeveloper Directoryを使用しているとは限りません。
xcode-select -p
xcodebuild -version
xcrun --find actool
xcrun actool --version
ノードに複数のXcodeバージョンを残している場合は、ジョブの開始時に DEVELOPER_DIR を明示的に設定し、xcodebuild -version の出力をビルドログへ記録します。スクリプトで指定するプラットフォーム、最低OSバージョン、デバイス種別、AppIcon名も、プロジェクトのビルド設定と一致させる必要があります。設定が異なると、事前検査に合格しても、別のパラメータセットでコンパイルできることしか確認できず、実際のtargetが有効であることの証明にはなりません。
actool を実行する前に、ディレクトリ内のJSONも簡易検査します。
find App/Assets.xcassets -name Contents.json -print0 |
while IFS= read -r -d '' file; do
plutil -lint "$file"
done
この手順により、マージコンフリクトの残骸、ファイル末尾の余分な文字、破損したファイルを具体的なパスまで正確に特定できます。
CIで直接実行できるスクリプトを作成する
次のスクリプトでは、出力先を .ci-artifacts/actool に固定し、失敗時の診断情報をすべて保存します。これにより、CIは診断結果を添付ファイルとして収集できます。ディレクトリ、デプロイターゲット、AppIcon名は引数またはプロジェクト設定から渡し、用途ごとにスクリプトを複製して管理しないようにします。
#!/bin/bash
set -euo pipefail
CATALOG="${1:-App/Assets.xcassets}"
DEPLOYMENT_TARGET="${DEPLOYMENT_TARGET:-16.0}"
APP_ICON_NAME="${APP_ICON_NAME:-AppIcon}"
OUT=".ci-artifacts/actool"
rm -rf "$OUT"
mkdir -p "$OUT/compiled"
export LANG=en_US.UTF-8
if ! xcrun actool "$CATALOG" \
--compile "$OUT/compiled" \
--platform iphoneos \
--minimum-deployment-target "$DEPLOYMENT_TARGET" \
--target-device iphone \
--app-icon "$APP_ICON_NAME" \
--output-partial-info-plist "$OUT/partial-info.plist" \
--notices --warnings --errors \
>"$OUT/actool.log" 2>&1; then
cat "$OUT/actool.log"
exit 1
fi
cat "$OUT/actool.log"
plutil -lint "$OUT/partial-info.plist"
set -euo pipefail を使用すると、コマンドが失敗した後もパイプラインが処理を続けてしまう事態を防げます。ログを先にファイルへ書き込み、その後で出力するのは、終了コードと診断内容の両方を保持するためです。コマンドを複雑なパイプへ直接つなぐと、最後に実行されたコマンドの終了ステータスを誤って取得しやすくなります。
警告とよくある誤検出に対処する
すべての警告をすぐに失敗扱いにしない
古いプロジェクトには、デバイスの外観設定が割り当てられていない項目、未使用の画像セット、命名に関する警告などが蓄積している場合があります。初回導入時はログを保存してベースラインを作成し、その後、確実に問題と判断できる項目だけをブロック対象にします。warning: を含むすべての行で失敗させると、多くの場合、チームはリソースを修正するより先に検査そのものを無効化してしまいます。
無効なJSON、リソースファイルの欠落、AppIconの必須スロットの欠落、名前の重複は、直ちにブロックすることを推奨します。未使用リソースなどの警告は、まず件数を記録し、整理期限を設定してから段階的にルールを厳格化します。
シミュレータと実機のプラットフォームを区別する
iphonesimulator だけで事前検査に合格しても、iphoneos でも必ず成功するとは限りません。デバイス要件、アイコンの規則、成果物の形式が異なるため、それぞれで異なる診断が発生する可能性があります。パイプラインが両方のtargetをビルドする場合は、検査も2回に分けて実行し、後の結果が前の結果を上書きしないよう、出力ディレクトリも分離します。
iPadを含むtargetでは、対応する --target-device ipad も追加します。検査を通すために iphone だけを指定してはいけません。デバイスの組み合わせは、targetの実際の設定に基づく必要があります。
パイプラインへ組み込み、結果を検証する
actool が通常依存するのはXcodeとリポジトリ内のリソースだけなので、事前検査を依存関係のインストール後に配置する必要はありません。より適切なのは、コードのチェックアウト完了後、依存関係の解決前です。これにより、パッケージ管理サービスが一時的に遅くなっていても、アセットエラーをすばやく通知できます。
初回導入時には、3つの障害サンプルを用意します。1つの Contents.json を壊す、参照されているアイコンファイルを一時的に移動する、AppIcon名を存在しない値に変更する、というケースです。各ジョブがゼロ以外のステータスで終了し、ログに具体的なディレクトリが含まれ、.ci-artifacts/actool がシステムによって保存されることを確認します。その後ファイルを元に戻し、事前検査と完全なビルドの両方が成功することを検証します。
リポジトリに複数のアプリtargetが含まれる場合は、「target、リソースディレクトリ、プラットフォーム、AppIcon名」の一覧を管理し、項目ごとにループ処理します。すべてのディレクトリを1つのコマンドへまとめて渡してはいけません。この構成なら、失敗情報を担当モジュールへ直接対応付けることができ、あるtargetのパラメータによって別のtargetの問題が隠れることもありません。
最終的には、2段階のゲートを維持します。actool はリソースディレクトリ内の確定的なエラーをすばやく検出し、完全なXcodeビルドはtargetの統合と最終成果物を検証します。両者の役割を明確に分けることで、アセットの問題がより早く表面化し、失敗ログも実際の修正箇所に近い情報を示すようになります。
よくある質問
actoolの事前検査だけでXcodeビルドを省略できますか?
できません。アセットと実行引数の問題は早期検出できますが、最終的には配布時と同じスキーム、SDK、構成でXcodeビルドを実行します。
actoolの警告はすべてCI失敗にするべきですか?
最初から一律に失敗させる必要はありません。既存警告を分類し、無効なJSONや必須アイコンの不足など確実な不備から阻止します。
次のビルドを専有Apple Silicon物理ノードで実行
タスクの規模に合わせて3つの構成と5つのノードから選択できます。計算リソースとストレージは他のテナントと共有されません。すべてのノードは365日、年間を通じて安定稼働しています。実際の利用可能状況はコンソールのリアルタイム表示をご確認ください。