Un même commit peut s’archiver correctement sur une machine de développement, puis utiliser une autre cible de déploiement ou omettre soudainement une architecture sur un Mac cloud HexVM. Dans ce cas, le problème vient souvent non pas du code source, mais des réglages de build finalement résolus par Xcode. Le fichier de projet, les fichiers .xcconfig, les variables d’environnement et les arguments de ligne de commande se remplacent successivement. Examiner un seul de ces éléments ne suffit donc pas à déterminer la configuration réellement appliquée à la cible.
Cette dérive n’entraîne pas nécessairement un échec immédiat. Elle peut d’abord modifier les artefacts, puis ne se révéler qu’au moment de la publication. Une méthode plus fiable consiste à conserver une référence nettoyée des réglages effectifs, puis à la régénérer et à la comparer avant chaque fusion et chaque archivage de production.
Définir le périmètre de configuration à auditer
N’enregistrez pas directement toute la sortie de xcodebuild. Les chemins absolus, les répertoires temporaires et les numéros de build produisent de nombreux écarts sans intérêt. Commencez par répartir les réglages en trois catégories.
| Type | Clés courantes | Traitement |
|---|---|---|
| Limites de l’artefact | PRODUCT_BUNDLE_IDENTIFIER, SUPPORTED_PLATFORMS, ARCHS |
Examiner toute modification |
| Comportement de compilation | SWIFT_VERSION, SWIFT_OPTIMIZATION_LEVEL, GCC_PREPROCESSOR_DEFINITIONS |
Examiner toute modification |
| Bruit propre au nœud | BUILD_DIR, TEMP_DIR, PROJECT_TEMP_DIR |
Supprimer ou normaliser |
Les réglages de signature doivent également faire partie de la référence. En revanche, ne stockez dans le dépôt ni fichiers de certificat, ni contenu de clés privées, ni identifiants temporaires. L’audit porte sur les noms de configuration, le mode de signature et les limites d’autorisation, pas sur la copie de données sensibles.
Une référence n’est pas un fichier de configuration « immuable ». Elle représente un état attendu soumis à la revue de code : les changements sont autorisés, mais ils doivent être visibles, explicables et réversibles.
Exporter les réglages réellement appliqués à la cible
Commencez par fixer le projet, le Scheme, la configuration de build et la plateforme cible. Pour un projet utilisant un workspace, remplacez -project par -workspace sans modifier les autres paramètres.
mkdir -p .ci/build-settings
xcodebuild \
-project App.xcodeproj \
-scheme App \
-configuration Release \
-destination 'generic/platform=iOS' \
-showBuildSettings \
> .ci/build-settings/raw.txt
Avant l’exécution, utilisez xcodebuild -list -project App.xcodeproj pour vérifier que le Scheme est partagé. Si le pipeline transmet des paramètres supplémentaires, tels que des feature flags ou un SYMROOT personnalisé, la génération de la référence et le build doivent employer exactement les mêmes paramètres. Dans le cas contraire, la comparaison portera sur deux contextes différents.
Un projet comportant plusieurs Targets produit plusieurs groupes de clés homonymes. Ne les dédupliquez pas aveuglément : conservez les en-têtes de Target ou créez un fichier distinct pour chaque Scheme. Les cibles de l’application, des extensions et des tests peuvent légitimement utiliser des périmètres de déploiement différents.
Éliminer le bruit des chemins et produire une référence stable
Le script ci-dessous extrait les lignes au format KEY = VALUE, supprime les clés associées aux répertoires temporaires et remplace le répertoire de travail ainsi que le répertoire utilisateur par des marqueurs stables. Il n’analyse aucune clé secrète et n’affiche pas l’ensemble des variables d’environnement.
from pathlib import Path
import os
source = Path(".ci/build-settings/raw.txt")
target = Path(".ci/build-settings/current.txt")
ignored = {
"BUILD_DIR",
"BUILD_ROOT",
"CONFIGURATION_BUILD_DIR",
"DERIVED_FILES_DIR",
"PROJECT_TEMP_DIR",
"TARGET_TEMP_DIR",
"TEMP_DIR"
}
root = str(Path.cwd())
home = str(Path.home())
rows = []
for line in source.read_text().splitlines():
stripped = line.strip()
if " = " not in stripped:
continue
key, value = stripped.split(" = ", 1)
if key in ignored:
continue
value = value.replace(root, "<ROOT>").replace(home, "<HOME>")
rows.append(f"{key}={value}")
target.write_text("
".join(sorted(rows)) + "
")
Après avoir validé le premier résultat, copiez current.txt vers une référence nommée selon son usage, par exemple release-ios.txt, puis ajoutez-la au dépôt.
python3 .ci/normalize_build_settings.py
cp .ci/build-settings/current.txt \
.ci/build-settings/release-ios.txt
Si les chemins des dépendances changent encore fréquemment, déterminez d’abord s’ils influencent les entrées de compilation. Ne filtrez pas HEADER_SEARCH_PATHS, FRAMEWORK_SEARCH_PATHS ou OTHER_SWIFT_FLAGS dans le seul but d’obtenir « zéro différence » : les changements apportés à ces clés sont souvent précisément ceux que l’audit doit détecter.
Transformer les écarts en contrôle bloquant du pipeline
Le contrôle doit s’exécuter après la résolution des dépendances et avant l’archivage de production. Il dispose ainsi de tous les chemins de recherche tout en pouvant interrompre une configuration incorrecte avant le lancement de l’étape d’archivage, plus coûteuse.
set -euo pipefail
xcodebuild \
-project App.xcodeproj \
-scheme App \
-configuration Release \
-destination 'generic/platform=iOS' \
-showBuildSettings \
> .ci/build-settings/raw.txt
python3 .ci/normalize_build_settings.py
diff -u \
.ci/build-settings/release-ios.txt \
.ci/build-settings/current.txt
Lorsque diff renvoie un état différent de zéro, le pipeline doit s’arrêter et conserver le texte des différences. Ne remplacez pas automatiquement la référence par le nouveau fichier : le contrôle bloquant ne serait alors plus qu’un simple enregistreur.
Définir une liste de réglages autorisés
L’équipe peut maintenir une liste d’exclusion explicite pour les clés dont l’innocuité est établie. Cette liste doit toutefois rester courte et préciser la raison de chaque exclusion. Il est recommandé de continuer à bloquer les modifications suivantes :
IPHONEOS_DEPLOYMENT_TARGETetSUPPORTED_PLATFORMSARCHS,EXCLUDED_ARCHSetONLY_ACTIVE_ARCHSWIFT_VERSIONet le niveau d’optimisationCODE_SIGN_STYLEet la méthode de sélection de l’identité de signaturePRODUCT_BUNDLE_IDENTIFIERet le chemin du fichier d’autorisationsDEBUG_INFORMATION_FORMATet les paramètres de l’éditeur de liens
Diagnostiquer les faux positifs courants et les dérives réelles
Si chaque exécution produit de nombreux écarts de chemins, vérifiez d’abord que le remplacement du répertoire racine couvre aussi les chemins obtenus après résolution des liens symboliques. Vous pouvez enregistrer pwd -P au début de la tâche et demander au script de normaliser à la fois les chemins logiques et les chemins réels.
Si aucune différence n’apparaît en local, mais que l’environnement cloud en signale constamment, vérifiez dans cet ordre : le même Scheme est-il utilisé, le nom de la configuration est-il identique, des réglages sont-ils ajoutés en ligne de commande, la résolution des dépendances est-elle terminée et des variables d’environnement interviennent-elles dans l’expansion des fichiers .xcconfig ? Ne modifiez pas d’abord la référence pour l’adapter au résultat.
Lorsqu’un changement réel est détecté, identifiez d’abord son origine avant de mettre à jour la référence :
- Utilisez
xcodebuild -showBuildSettingspour déterminer le Target concerné. - Recherchez la clé correspondante dans le fichier de projet, les fichiers
.xcconfiget les paramètres du pipeline. - Expliquez l’objectif du changement et ses effets sur Debug et Release.
- Validez-le au moyen d’un build propre et d’un archivage.
- Soumettez la modification de configuration et la mise à jour de la référence dans la même revue.
Mettre en place un rythme d’audit durable
Les références de configuration doivent être séparées par Scheme, configuration de build et plateforme. N’utilisez pas un seul fichier pour couvrir tous les scénarios. Pour les commits courants, vous pouvez vérifier la référence Release de l’application principale. Exécutez les contrôles correspondants lorsque les modifications concernent des extensions, des bundles de test ou le processus de publication.
Après chaque évolution de la stratégie de version de Xcode, régénérez les réglages et examinez chaque différence, car les valeurs par défaut peuvent changer. L’essentiel n’est pas le nombre d’écarts, mais ceux qui modifient les entrées de compilation, la structure des artefacts, les limites de signature ou la cible d’exécution. Une fois cette chaîne de preuves conservée, il n’est plus nécessaire d’examiner toute la machine pour expliquer un échec de build, et les changements de configuration deviennent aussi traçables que les changements du code source.
Questions fréquentes
Pourquoi ne pas comparer uniquement le projet et les fichiers xcconfig ?
Ils décrivent les entrées. La sortie de xcodebuild résout aussi l’héritage, les valeurs conditionnelles et les surcharges de ligne de commande, donc elle reflète mieux le build réel.
Quels changements doivent bloquer la CI ?
Les changements de cible de déploiement, d’architecture, de version Swift, d’optimisation, de signature et d’identifiant produit doivent généralement être bloquants. Les chemins temporaires sont normalisés.
Choisissez un Mac dans le cloud dédié pour votre prochaine compilation
Choisissez le modèle de Mac mini, la durée de location et la région selon votre charge de travail. Chaque commande correspond à un nœud physique dédié ; la disponibilité réelle est indiquée en temps réel dans la console.