Dossier d’ingénierie

Construire une baseline d’analyse statique Xcode sur un Mac cloud

Construire une baseline d’analyse statique Xcode sur un Mac cloud

Lorsqu’une équipe intègre xcodebuild analyze à un Mac cloud, le résultat le plus courant n’est pas l’absence immédiate de défauts, mais l’apparition simultanée de dizaines de diagnostics historiques. Si le nombre total d’avertissements devient un critère d’échec, le pipeline risque de rester durablement au rouge. Si les journaux sont simplement conservés comme artefacts, personne ne les consulte régulièrement. Une approche plus opérationnelle consiste à établir d’abord une baseline révisée, puis à ne rendre chaque fusion responsable que des nouveaux problèmes qu’elle introduit.

Commencer par figer les entrées de l’analyse

L’analyse statique dépend de la configuration du projet, des conditions de compilation et des fichiers sources réellement compilés. Avant de lancer l’analyse, il faut figer la version de Xcode sélectionnée, le workspace, le Scheme, la configuration et la plateforme cible. Il faut également s’assurer que la CI utilise les mêmes fichiers de verrouillage des dépendances que la tâche d’archivage. N’utilisez pas le Scheme personnel d’un développeur et ne sélectionnez pas automatiquement la chaîne d’outils « la plus récente » dans un script.

La tâche d’analyse doit utiliser un répertoire dédié afin de ne pas modifier DerivedData en même temps que les tâches de compilation ou de test :

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 ne convient qu’aux cibles d’analyse qui peuvent être traitées sans signature. Si un script du projet lit explicitement des paramètres de compilation liés à la signature, corrigez d’abord les limites d’entrée du script au lieu d’ajouter aveuglément d’autres paramètres de substitution.

Une baseline n’est pas une liste de problèmes que l’on peut ignorer, mais un instantané des faits connus pendant une migration. Les nouveaux problèmes doivent toujours provoquer un échec immédiat, tandis que les problèmes existants doivent avoir un responsable et un plan de correction.

Conserver les résultats complets au lieu de compter les warnings

Exécuter directement grep -c warning: est pratique, mais cette méthode ne convient pas à un contrôle qualité durable. Les chemins, les numéros de ligne et le format des journaux peuvent changer, et un même diagnostic peut être affiché plusieurs fois. La CI doit conserver l’intégralité du fichier .xcresult et du journal brut, puis produire à partir des résultats structurés une liste de diagnostics destinée à la comparaison.

Les sous-commandes de xcresulttool peuvent varier selon la version de Xcode. Commencez donc par enregistrer l’aide et revérifiez le script d’analyse syntaxique à chaque mise à jour de l’image du nœud :

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

Si la chaîne d’outils actuelle n’accepte pas --legacy, adaptez la commande en suivant l’aide de cette version et incluez la modification de l’analyseur dans la même demande de fusion que la mise à niveau de la chaîne d’outils. Si le script échoue, ne revenez pas à un comptage dans les journaux tout en laissant la tâche réussir : le contrôle qualité cesserait alors de fonctionner sans signalement.

Il est recommandé de conserver les champs suivants pour chaque diagnostic :

Champ Utilité
Règle ou type de problème Distingue les catégories telles que les pointeurs nuls et les fuites de ressources
Chemin relatif au dépôt Empêche le répertoire de travail du nœud d’entrer dans la baseline
Nom de fonction ou de symbole Facilite la localisation même après un changement de numéro de ligne
Message normalisé Supprime les répertoires temporaires et les nombres instables
Niveau de gravité Permet de définir une stratégie de traitement selon le risque

Produire une baseline révisable avec des empreintes stables

L’empreinte d’un diagnostic ne doit pas contenir de chemin absolu, de chemin DerivedData ni un numéro de ligne pris isolément. Une combinaison plus fiable est « type de problème + chemin relatif au dépôt + nom du symbole + message normalisé ». Le numéro de ligne peut être conservé pour l’affichage, mais ne doit pas participer à la clé primaire. Sinon, l’ajout d’une seule ligne en haut d’un fichier ferait passer tous les problèmes existants pour de nouveaux problèmes.

Éviter une normalisation excessive

Vous pouvez supprimer les préfixes des répertoires de travail, les UUID temporaires et les espaces consécutifs, mais ne supprimez pas les noms de variables, les noms d’appels ou les types de ressources. Si deux défauts distincts dans un même fichier sont réduits à une seule empreinte, un problème apparu ultérieurement peut être masqué par la baseline existante.

Il est recommandé d’utiliser pour la baseline un fichier JSON Lines trié, avec un enregistrement par ligne, et de l’intégrer au contrôle de version :

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

Lors de la création initiale de la baseline, les responsables du code doivent vérifier chaque entrée. Ils doivent au minimum confirmer que le diagnostic provient bien de la branche principale actuelle, qu’il ne peut pas être corrigé immédiatement, qu’un élément de suivi existe et que la baseline ne contient ni échec d’analyse syntaxique ni doublon.

Comparer uniquement les différences d’ensembles dans les demandes de fusion

Après la génération de current.jsonl par chaque tâche, comparez son ensemble d’empreintes à celui de baseline.jsonl. current - baseline correspond aux nouveaux problèmes et doit faire échouer la tâche. baseline - current correspond aux problèmes disparus et doit inviter les responsables à supprimer les entrées correspondantes de la baseline plutôt qu’à conserver des enregistrements obsolètes.

La sortie du contrôle qualité doit être concise et exploitable. Pour chaque nouveau problème, affichez le type, le chemin relatif, le numéro de ligne, le symbole et le message, ainsi que l’emplacement de l’artefact contenant le xcresult complet. La console ne doit présenter qu’un résumé afin que des milliers de lignes de traces d’analyse ne masquent pas les différences importantes.

Distinguer trois modes d’échec

Le script d’analyse doit au minimum distinguer les états suivants :

  1. xcodebuild analyze a lui-même échoué, par exemple parce qu’une dépendance manque ou que le Scheme n’est pas disponible ;
  2. L’analyse syntaxique des résultats a échoué, par exemple parce qu’une mise à niveau de la chaîne d’outils a modifié la structure JSON ;
  3. L’analyse a réussi, mais elle a détecté de nouveaux diagnostics absents de la baseline.

Les deux premiers cas correspondent à des erreurs d’infrastructure ou de configuration et ne doivent pas être présentés comme « zéro nouveau défaut ». La tâche ne peut réussir que si l’analyse et l’analyse syntaxique aboutissent toutes les deux et que la différence entre les ensembles est vide.

Réduire continuellement la baseline

Si la baseline reste longtemps inchangée, elle finira par devenir une nouvelle liste d’exclusions que personne ne maintient. Vous pouvez désigner un responsable pour chaque module et corriger les diagnostics voisins lors de toute modification des fichiers concernés. Vous pouvez aussi prévoir la suppression d’un petit nombre d’éléments à haut risque à chaque itération. Une fois un problème corrigé, son enregistrement doit également être supprimé de la baseline. L’exécution suivante vérifiera que le problème a réellement disparu.

Les vérifications courantes peuvent suivre cet ordre :

  • Vérifier que la chaîne d’outils, le Scheme, la configuration et la plateforme cible n’ont pas dérivé ;
  • Vérifier que le répertoire d’analyse est isolé des autres tâches ;
  • Vérifier que le xcresult, le journal et la liste normalisée ont tous été générés ;
  • Contrôler quelques empreintes pour s’assurer qu’elles ne contiennent ni chemin absolu ni identifiant temporaire ;
  • Bloquer les fusions en présence de nouveaux diagnostics et signaler les diagnostics disparus afin de réduire la baseline ;
  • Lors d’une mise à niveau de la chaîne d’outils, reconstruire d’abord la baseline sur une branche distincte et réviser les différences.

Pour exécuter ce type de tâche sur OakVM, l’essentiel n’est pas de choisir un seuil plus strict, mais de préserver la traçabilité de la chaîne d’outils, des scripts et du format des résultats sur un même nœud physique. Tant qu’un échec d’analyse syntaxique ne peut pas être confondu avec une réussite et que toute modification de la baseline doit être révisée, l’analyse statique peut passer d’un long journal facile à ignorer à un contrôle qualité incrémental et fiable.

Questions fréquentes

Pourquoi ne pas imposer immédiatement zéro avertissement ?

Un projet ancien peut contenir des constats connus. Une règle immédiatement stricte rend la CI rouge en permanence. La baseline permet de bloquer les nouveaux défauts puis de réduire progressivement la dette existante.

Quels fichiers faut-il conserver après l’analyse ?

Conservez le bundle xcresult complet, le journal de compilation, la liste normalisée des diagnostics et la baseline utilisée. Ces éléments permettent de reproduire et d’examiner chaque échec.

OAKVM BUILD NODE

Lancez votre prochain build sur un nœud physique dédié

Choisissez OakVM M4 ou OakVM M4 Pro et louez un Mac cloud non virtualisé à la journée, à la semaine, au mois ou au trimestre. La disponibilité réelle du nœud est indiquée en temps réel dans la console.

Choisir une configuration et louer