Un projet Xcode maintenu depuis plusieurs années finit souvent par accumuler des dizaines d’avertissements de compilation. Activer directement SWIFT_TREAT_WARNINGS_AS_ERRORS rendrait immédiatement la branche principale impossible à compiler. Mais si rien n’est fait, de nouveaux avertissements continueront à se mêler au bruit existant. Une approche plus réaliste consiste à figer, sur un Mac dans le cloud, une référence d’avertissements préalablement révisée, afin que le CI refuse uniquement les nouveaux avertissements introduits par la modification en cours.
Définir d’abord le périmètre du contrôle
Le contrôle ne doit pas se limiter à compter les occurrences de warning: dans les journaux. Le téléchargement des dépendances, les scripts de compilation et les outils système peuvent tous produire le même texte. Un simple comptage est donc instable et n’indique pas au développeur quel fichier corriger.
Il est recommandé de normaliser chaque avertissement sous la forme « chemin relatif au dépôt + texte de l’avertissement », en supprimant les numéros de ligne et de colonne, les répertoires temporaires et les horodatages. Ainsi, déplacer du code ne crée pas de nouvelle signature, tandis qu’un renommage de fichier ou une modification du message d’avertissement déclenche toujours une révision.
| Contenu | Inclus dans la signature | Motif |
|---|---|---|
| Chemin relatif au dépôt | Oui | Identifie le fichier concerné |
| Texte de l’avertissement | Oui | Distingue le type de problème |
| Numéros de ligne et de colonne | Non | Ils changent facilement après une modification du code |
| Chemin DerivedData | Non | Il peut varier à chaque tâche |
| Avertissements des dépendances externes | Non par défaut | L’équipe ne peut généralement pas les corriger directement |
La référence représente une « dette technique actuellement connue et acceptée » ; elle ne signifie pas que ces avertissements sont légitimes. Chaque suppression d’un avertissement existant doit également réduire la référence.
Générer des signatures d’avertissement reproductibles
Commencez par fixer l’espace de travail, le Scheme, la configuration et le SDK. Si les postes de développement utilisent Debug tandis que le CI utilise Release, les chemins de compilation conditionnelle diffèrent et les références obtenues ne sont pas comparables. Le script ci-dessous impose à l’appelant de fournir explicitement l’espace de travail et le Scheme, puis conserve la sortie complète.
#!/bin/bash
set -u
WORKSPACE="${WORKSPACE:?set WORKSPACE}"
SCHEME="${SCHEME:?set SCHEME}"
ROOT="$(git rev-parse --show-toplevel)"
OUT="$ROOT/.ci-artifacts"
DERIVED="$OUT/DerivedData"
mkdir -p "$OUT"
rm -rf "$DERIVED"
set +e
xcodebuild \
-workspace "$WORKSPACE" \
-scheme "$SCHEME" \
-configuration Release \
-sdk iphoneos \
-derivedDataPath "$DERIVED" \
CODE_SIGNING_ALLOWED=NO \
build 2>&1 | tee "$OUT/xcodebuild.log"
BUILD_STATUS=${PIPESTATUS[0]}
set -e
grep -F "$ROOT/" "$OUT/xcodebuild.log" \
| grep " warning: " \
| sed "s#${ROOT}/##" \
| sed -E 's#:[0-9]+:[0-9]+: warning: # | #' \
| LC_ALL=C sort -u \
> "$OUT/warnings.current"
exit "$BUILD_STATUS"
CODE_SIGNING_ALLOWED=NO convient aux tâches qui vérifient uniquement la compilation. Il ne doit pas être repris tel quel dans un pipeline chargé de créer une archive ou de valider la signature. Les échecs de compilation et les régressions d’avertissements doivent également être traités séparément : si xcodebuild échoue, son code de sortie doit être renvoyé en priorité, au lieu de masquer l’erreur réelle avec un fichier d’avertissements vide.
Gérer les scripts et les diagnostics multilignes
Certaines phases Run Script produisent des avertissements personnalisés dont le format ne contient pas nécessairement de coordonnées dans le code source. Si ces scripts sont maintenus par l’équipe, définissez pour eux un préfixe fixe, puis ajoutez une seconde règle d’analyse. Évitez toute expression régulière trop générale qui capturerait l’ensemble de la sortie, au risque d’inclure aussi les messages réseau et les notifications de mise à niveau des outils dans la référence.
Les lignes d’explication supplémentaires de Swift ne contiennent généralement pas warning: et ne doivent pas devenir des signatures indépendantes. Elles doivent toutefois rester dans le journal complet. Le contrôle sert à prendre rapidement une décision, tandis que le journal complet permet de reconstituer le contexte ; l’un ne remplace pas l’autre.
Réviser et valider la première référence
Le premier fichier warnings.current généré ne doit pas être copié automatiquement dans la référence. Commencez par attribuer les éléments à leurs responsables selon leur chemin, supprimez les doublons et vérifiez qu’aucun fichier dérivé, code source de dépendance ou répertoire temporaire n’a été inclus. Une fois la révision terminée, exécutez :
mkdir -p .ci
LC_ALL=C sort -u .ci-artifacts/warnings.current > .ci/xcode-warnings.baseline
git add .ci/xcode-warnings.baseline
git commit -m "Add reviewed Xcode warning baseline"
La référence doit être versionnée avec le code. Tout commit qui l’agrandit doit présenter les nouvelles signatures et expliquer leur ajout. Une tâche en échec ne doit jamais « apprendre » automatiquement de nouveaux avertissements, sous peine de laisser passer précisément ce que le contrôle devait bloquer.
Lors d’une mise à niveau de Xcode, le compilateur peut modifier le libellé de ses diagnostics. La bonne procédure consiste à lancer une compilation complète dans une branche dédiée, à distinguer les nouveaux problèmes réels des simples changements de formulation, puis à mettre à jour la référence en une seule fois en consignant la version de la chaîne d’outils. Il ne faut pas ajouter au script de comparaison une correspondance approximative toujours plus permissive.
Bloquer uniquement les nouveaux avertissements dans le CI
Une fois le résultat actuel et la référence triés, comm permet d’identifier les signatures présentes uniquement dans la compilation actuelle :
BASELINE=".ci/xcode-warnings.baseline"
CURRENT=".ci-artifacts/warnings.current"
NEW=".ci-artifacts/warnings.new"
test -f "$BASELINE"
LC_ALL=C sort -u "$BASELINE" -o "$BASELINE"
LC_ALL=C sort -u "$CURRENT" -o "$CURRENT"
LC_ALL=C comm -13 "$BASELINE" "$CURRENT" > "$NEW"
if test -s "$NEW"; then
printf '%s
' "New Xcode warnings detected:"
cat "$NEW"
exit 42
fi
Les fichiers xcodebuild.log, warnings.current et warnings.new doivent être archivés ensemble. Le code de sortie 42 n’est qu’une convention interne à l’équipe ; l’essentiel est d’afficher « échec de compilation » et « nouvel avertissement » comme deux causes distinctes. En disposant du chemin et du texte de l’avertissement, le développeur peut effectuer la correction au cours d’une seule boucle de retour.
Si plusieurs tâches compilent différents Target en parallèle, chacune doit produire son propre résultat. Ces résultats seront ensuite fusionnés, triés et dédupliqués. Plusieurs processus ne doivent jamais écrire simultanément dans le même fichier, car les troncatures et les écritures entrelacées provoqueraient des erreurs de détection intermittentes.
Réduire continuellement la référence au lieu de la figer définitivement
Une fois le contrôle en place, chaque correction d’un ancien avertissement doit entraîner la suppression de la ligne correspondante dans la référence. Vous pouvez ajouter une vérification non bloquante qui repère les éléments encore présents dans la référence, mais désormais absents du résultat actuel, afin d’inviter l’auteur du commit à les supprimer. La référence diminue ainsi dans un seul sens, au lieu de devenir une liste historique que plus personne ne comprend.
Avant de considérer le système comme stable, quatre points doivent encore être vérifiés : le CI et les environnements locaux utilisent-ils la même version de Xcode ? Le Scheme contient-il les Target attendus ? Les paramètres régionaux modifient-ils la sortie des outils ? Le filtrage des chemins du dépôt couvre-t-il tout le code source interne ? Sur les nœuds OnceMac, le répertoire d’exécution doit également être créé de manière déterministe par chaque tâche, afin d’éviter de réutiliser les journaux et le DerivedData d’une compilation précédente.
Lorsque la référence atteint zéro, la comparaison des écarts peut être supprimée et la stratégie du compilateur qui traite les avertissements comme des erreurs peut être activée. D’ici là, ce contrôle fondé sur une référence constitue une transition pragmatique : il ne masque pas la dette existante et empêche toute nouvelle dette de s’accumuler.
Questions fréquentes
Pourquoi ne pas traiter immédiatement tous les avertissements comme des erreurs ?
Un projet qui possède déjà de nombreux avertissements verrait toutes ses compilations échouer. La référence bloque d’abord les régressions, puis le mode strict peut être activé une fois la dette supprimée.
Un changement de numéro de ligne provoque-t-il un faux positif ?
Oui si ce numéro reste dans la signature. Il faut retirer ligne et colonne, puis comparer uniquement le chemin relatif du fichier et le texte de l’avertissement.
Faut-il inclure les avertissements des dépendances ?
En général, non. Le filtre doit viser les sources maintenues par l’équipe et n’ajouter que les dépendances internes couvertes par des règles de chemin explicites.
Lancez votre prochain build sur un nœud physique dédié.
Choisissez la puce, la mémoire, le stockage, le nœud et la durée de location pour profiter d’un Mac dans le cloud avec des ressources qui ne sont pas partagées avec d’autres clients.