エンジニアリング記録

クラウド Mac で Xcode 静的解析のベースラインを運用する

クラウド Mac で Xcode 静的解析のベースラインを運用する

チームがクラウド Mac 上の CI に xcodebuild analyze を組み込んだとき、最初から不具合がゼロになることはほとんどなく、たいていは過去から蓄積された診断が数十件まとめて表示されます。警告の総数をそのまま失敗条件にするとパイプラインは長期間赤いままになり、ログを成果物として保存するだけでは継続的に確認されません。より実践的なのは、レビュー済みのベースラインを先に作成し、以後のマージでは新しく増えた問題だけを失敗対象にする方法です。

解析タスクの入力を固定する

静的解析の結果は、プロジェクト設定、コンパイル条件、実際にコンパイル対象となるソースファイルに左右されます。解析前に、使用する Xcode、ワークスペース、Scheme、構成、ターゲットプラットフォームを固定し、CI ではアーカイブタスクと同じ依存関係のロックファイルを使用してください。開発者個人の Scheme を使ったり、スクリプトで「最新」のツールチェーンを自動選択したりしてはいけません。

解析タスクには専用ディレクトリを使用し、ビルドやテストのタスクと同時に DerivedData が書き換えられないようにします。

set -o pipefail

ROOT="$PWD"
OUT="$ROOT/ci-artifacts/analyze"
DERIVED="$ROOT/.derived/analyze"
RESULT="$OUT/AppAnalyze.xcresult"

rm -rf "$OUT" "$DERIVED"
mkdir -p "$OUT" "$DERIVED"

xcodebuild analyze \
  -workspace App.xcworkspace \
  -scheme App \
  -configuration Debug \
  -destination 'generic/platform=iOS' \
  -derivedDataPath "$DERIVED" \
  -resultBundlePath "$RESULT" \
  CODE_SIGNING_ALLOWED=NO \
  | tee "$OUT/xcodebuild.log"

CODE_SIGNING_ALLOWED=NO が使えるのは、解析の完了に署名を必要としないターゲットだけです。プロジェクト内のスクリプトが署名関連のビルド設定を明示的に参照している場合は、上書きパラメータをむやみに追加するのではなく、まずスクリプトの入力境界を修正してください。

ベースラインは「無視してよい問題」の一覧ではなく、移行期間中に既知の事実を記録したスナップショットです。新しい問題は引き続き直ちに失敗させ、既存の問題には担当者と解消計画を設定する必要があります。

warning の件数だけでなく完全な結果を保存する

grep -c warning: を直接実行する方法は手軽ですが、長期的なゲートには適していません。ログ内のパス、行番号、書式は変化する可能性があり、同じ診断が複数回出力されることもあります。CI では .xcresult と生ログを完全な形で保存し、構造化された結果から比較用の診断一覧を生成する必要があります。

Xcode のバージョンによって xcresulttool のサブコマンドが変更される可能性があるため、まずヘルプ情報を記録し、ノードイメージを更新する際に解析スクリプトを再確認します。

xcrun xcresulttool help > "$OUT/xcresulttool-help.txt"
xcrun xcresulttool get \
  --legacy \
  --path "$RESULT" \
  --format json > "$OUT/xcresult.json"

現在のツールチェーンが --legacy を受け付けない場合は、そのバージョンのヘルプに従ってコマンドを調整し、パーサーの変更とツールチェーンのアップグレードを同じマージリクエストに含めてください。スクリプトが失敗したときにログの件数集計へフォールバックして処理を通してはいけません。そうすると、ゲートが気付かないうちに無効になります。

各診断には、次のフィールドを保持することを推奨します。

フィールド 用途
ルールまたは問題種別 null ポインタやリソースリークなどの分類を区別する
リポジトリからの相対パス ノードの作業ディレクトリがベースラインに混入するのを防ぐ
関数名またはシンボル名 行番号が変わっても位置の特定に役立つ
正規化されたメッセージ 一時ディレクトリや不安定な数値を除去する
重大度 リスクに応じた処理方針を設定できるようにする

安定したフィンガープリントでレビュー可能なベースラインを作成する

診断のフィンガープリントに、絶対パス、DerivedData のパス、行番号だけを含めるべきではありません。比較的安定する組み合わせは、「問題種別 + リポジトリからの相対パス + シンボル名 + 正規化されたメッセージ」です。行番号は表示用として保持できますが、主キーには含めないでください。ファイルの先頭に 1 行追加しただけで、既存の問題がすべて新規と判定されてしまうためです。

正規化でまとめすぎないようにする

作業ディレクトリのプレフィックス、一時 UUID、連続する空白は削除できますが、変数名、呼び出し名、リソース種別は削除しないでください。同じファイル内で発生した別々の不具合が同じフィンガープリントにまとめられると、後から発生した問題が既存のベースラインに隠れてしまいます。

ベースラインファイルには、1 行につき 1 レコードの、ソート済み JSON Lines を使用し、バージョン管理に含めることを推奨します。

{"fingerprint":"sha256:…","type":"AnalyzeWarning","path":"Sources/Cache.swift","symbol":"load()","message":"Potential leak of an object"}

最初にベースラインを作成するときは、コードオーナーが各項目を個別に確認する必要があります。少なくとも、その診断が現在のメインブランチから実際に発生していること、すぐには修正できないこと、追跡項目が作成済みであること、解析失敗や重複レコードがベースラインに含まれていないことを確認してください。

マージリクエストでは集合の差分だけを比較する

タスクごとに current.jsonl を生成したら、そのフィンガープリント集合を baseline.jsonl と比較します。current - baseline は新しい問題なので、タスクを失敗させます。baseline - current はすでに解消した問題なので、無効なレコードを残し続けるのではなく、対応するベースライン項目を削除するようメンテナーへ通知します。

ゲートの出力は短く、すぐに対応できる内容にしてください。新しい問題ごとに、種別、相対パス、行番号、シンボル、メッセージを表示し、完全な xcresult 成果物の保存場所も示します。コンソールログには要約だけを表示し、数千行に及ぶ解析過程によって本当に重要な差分が埋もれないようにします。

3 種類の失敗を区別する

解析スクリプトでは、少なくとも次の状態を区別する必要があります。

  1. 依存関係の不足や Scheme が利用できないなど、xcodebuild analyze 自体が失敗した場合。
  2. ツールチェーンのアップグレードで JSON 構造が変わったなど、結果の解析に失敗した場合。
  3. 解析には成功したものの、ベースラインにない新しい診断が見つかった場合。

最初の 2 つはインフラストラクチャまたは構成のエラーであり、「新規不具合ゼロ」と表示してはいけません。解析と結果のパースが両方とも成功し、集合の差分が空の場合に限って、タスクを成功と判定できます。

ベースラインを継続的に縮小する

ベースラインが長期間変わらなければ、最終的には誰にも管理されない別の無視リストになります。モジュールごとに担当者を指定し、関連ファイルを変更するときに周辺の診断も修正する方法があります。また、イテレーションごとにリスクの高い項目を少数ずつ解消することもできます。修正後はベースラインのレコードも必ず削除し、次回の実行で問題が本当に解消したかを検証してください。

日常的な確認は、次の順序で実施できます。

  • ツールチェーン、Scheme、構成、ターゲットプラットフォームにずれがないことを確認する。
  • 解析ディレクトリが他のタスクから分離されていることを確認する。
  • xcresult、ログ、正規化済み一覧がすべて生成されていることを確認する。
  • フィンガープリントに絶対パスや一時識別子が含まれていないかを抜き取り確認する。
  • 新しい診断ではマージをブロックし、消えた診断ではベースラインの縮小を促す。
  • ツールチェーンをアップグレードするときは、まず独立したブランチで再生成し、差分をレビューする。

OakVM 上でこの種のタスクを実行する際に重要なのは、より厳しいしきい値を選ぶことではなく、同じ物理ノード上のツールチェーン、スクリプト、結果形式を追跡可能な状態に保つことです。解析失敗が成功として扱われず、ベースラインの変更が必ずレビューされる仕組みさえあれば、静的解析は見落とされがちな長いログから、安定した増分品質ゲートへ変わります。

よくある質問

最初から静的解析の警告をゼロにするべきですか?

既存プロジェクトでは過去の指摘が多く、常に失敗するゲートは無視されがちです。まず既知の結果をベースライン化し、新規指摘を止めながら段階的に減らす方法が実用的です。

解析ジョブでは何を成果物として保存しますか?

完全な xcresult、ビルドログ、正規化した診断一覧、比較に使ったベースラインを保存します。失敗時の文脈確認と再比較の両方に必要です。

OAKVM BUILD NODE

次のビルドを専用物理ノードで実行

OakVM M4またはOakVM M4 Proを選択し、仮想マシンではないクラウド Macを日単位・週単位・月単位・四半期単位でレンタルできます。ノードの実際の利用可否はコンソールのリアルタイム情報をご確認ください。

構成を選んでレンタル