Guides techniques MangoVM

Séparer les environnements iOS avec xcconfig et auditer l’archive

Séparer les environnements iOS avec xcconfig et auditer l’archive

Lorsqu’un même projet iOS se connecte aux services de développement, de préproduction et de production, l’erreur la plus dangereuse n’est généralement pas un échec de compilation, mais la création réussie d’une archive pour le mauvais environnement. Une adresse de développement, une journalisation détaillée ou un indicateur de fonctionnalité interne peut se retrouver dans le paquet publié alors que la pipeline reste au vert. Une approche plus fiable consiste à organiser les réglages publics par couches, à figer le point d’entrée du build et à intégrer le contrôle de l’archive au processus de publication, plutôt que de compter sur un développeur pour choisir le bon menu dans Xcode.

Définir d’abord la frontière entre configuration et secrets

xcconfig convient aux paramètres de build qui peuvent être exposés avec le client, tels que l’URL de base de l’API, le suffixe du Bundle ID, le niveau de journalisation et la valeur par défaut des indicateurs de fonctionnalité. Ce n’est pas un coffre-fort à secrets. Toute valeur injectée dans le processus de build du client peut apparaître dans Info.plist, dans les ressources, dans les arguments de compilation ou dans l’exécutable.

Le critère est simple : si l’utilisateur ne doit pas connaître une valeur après avoir récupéré le paquet d’installation, ne la confiez pas à Xcode pour l’intégrer à l’App.

Les clés d’administration côté serveur, les identifiants du service de signature et les jetons disposant de droits en écriture doivent rester sur le serveur ou dans un magasin d’identifiants CI à accès contrôlé. Le client ne doit recevoir que des autorisations temporaires, à faibles privilèges et révocables. Même si la CI injecte un secret par une variable d’environnement, cela ne change rien au fait que le livrable final peut être analysé.

Commencez par dresser l’inventaire des réglages, puis indiquez pour chacun son caractère public ou non, sa source et son emplacement attendu après l’archivage. C’est cette liste que l’équipe doit examiner, et non les dizaines de valeurs dispersées dans les Build Settings.

Mettre en place des couches xcconfig faciles à auditer

Il est recommandé de séparer les réglages communs, les différences propres à chaque environnement et les surcharges locales :

Config/
  Base.xcconfig
  Development.xcconfig
  Staging.xcconfig
  Production.xcconfig
  LocalOverrides.xcconfig.example

Base.xcconfig ne doit contenir que les valeurs communes à tous les environnements. Chaque fichier d’environnement l’inclut d’abord, puis redéfinit uniquement les quelques valeurs qui diffèrent :

#include "Base.xcconfig"

APP_ENVIRONMENT = staging
API_BASE_URL = https:/$()/staging-api.invalid
ENABLE_VERBOSE_LOGGING = YES
PRODUCT_BUNDLE_IDENTIFIER = com.example.product.staging

Ici, $() empêche les deux barres obliques de l’URL d’être interprétées comme le début d’un commentaire. Le domaine d’exemple est une valeur documentaire volontairement non résoluble ; dans un projet réel, remplacez-le par l’adresse de votre propre service.

Le fichier de surcharge locale ne doit pas être ajouté au dépôt. Une inclusion facultative permet de simplifier la première récupération du projet :

#include? "LocalOverrides.xcconfig"

Les surcharges locales doivent toutefois rester un outil de confort pour le développement et ne jamais devenir une entrée implicite des archives officielles. La configuration de production doit être entièrement résolue même en l’absence de ce fichier. Associez les trois Configuration Development, Staging et Release aux fichiers correspondants, puis vérifiez que le Scheme utilisé par la CI est défini comme Shared.

Figer le point d’entrée du build en ligne de commande

Sur un Mac dans le cloud, une tâche d’archivage ne doit pas hériter de l’état laissé par une précédente manipulation dans l’interface graphique. Le workspace, le scheme, la configuration et le chemin de sortie doivent tous être fournis explicitement :

set -euo pipefail

WORKSPACE="App.xcworkspace"
SCHEME="App"
CONFIGURATION="Release"
ARCHIVE_PATH="$PWD/build/App.xcarchive"

xcodebuild \
  -workspace "$WORKSPACE" \
  -scheme "$SCHEME" \
  -configuration "$CONFIGURATION" \
  -showBuildSettings > build-settings.txt

xcodebuild \
  -workspace "$WORKSPACE" \
  -scheme "$SCHEME" \
  -configuration "$CONFIGURATION" \
  -archivePath "$ARCHIVE_PATH" \
  clean archive

Vérifier les valeurs résolues avant l’archivage

Ne contrôlez pas uniquement les fichiers sources : vérifiez les réglages effectivement résolus par Xcode. Vous pouvez extraire les éléments essentiels de build-settings.txt et imposer des assertions strictes pour la production :

grep -E "APP_ENVIRONMENT|API_BASE_URL|PRODUCT_BUNDLE_IDENTIFIER|ENABLE_VERBOSE_LOGGING" \
  build-settings.txt

grep -q "APP_ENVIRONMENT = production" build-settings.txt
grep -q "ENABLE_VERBOSE_LOGGING = NO" build-settings.txt

Si le projet comporte plusieurs Target, contrôlez chacun séparément, car les extensions, les bundles de test et l’App principale peuvent hériter de Base Configuration différentes. Le script doit également afficher le commit Git courant, la version de Xcode et le Scheme utilisé afin de pouvoir reproduire le contexte en cas d’écart.

Inspecter l’archive réellement livrée

Des Build Settings corrects ne garantissent pas que le livrable le soit aussi. Un Run Script, du code généré ou une étape de copie de ressources peut encore introduire une configuration de test dans l’archive. Une fois l’archivage terminé, contrôlez au minimum la liste de propriétés de l’App principale, les ressources intégrées et les chaînes présentes dans l’exécutable.

APP_PATH="$(find build/App.xcarchive/Products/Applications -maxdepth 1 -name '*.app' -print -quit)"
test -n "$APP_PATH"

plutil -p "$APP_PATH/Info.plist"

if grep -RInaE "staging-api|debug-token|INTERNAL_ONLY" "$APP_PATH"; then
  echo "forbidden marker found in archive" >&2
  exit 1
fi

EXECUTABLE_NAME=$(/usr/libexec/PlistBuddy -c "Print :CFBundleExecutable" "$APP_PATH/Info.plist")
if strings "$APP_PATH/$EXECUTABLE_NAME" | grep -E "staging-api|debug-token|INTERNAL_ONLY"; then
  echo "forbidden marker found in executable" >&2
  exit 1
fi

Les termes recherchés doivent être des marqueurs définis par l’équipe. N’inscrivez jamais de véritables secrets directement dans le script ou les journaux. Il est conseillé de maintenir deux courtes listes, l’une pour les éléments autorisés et l’autre pour les éléments interdits, afin d’éviter les faux positifs provoqués par des mots ordinaires. Les résultats de l’analyse ne doivent conserver que le chemin du fichier et le nom de la règle, sans réafficher l’intégralité du contenu sensible détecté.

Élément contrôlé Points à vérifier Traitement en cas d’échec
Build Settings finaux Nom de l’environnement, Bundle ID, journalisation Arrêter immédiatement l’archivage
Info.plist Adresse du service, URL Scheme, étiquette d’environnement Bloquer l’export
Répertoire des ressources de l’App Configuration de débogage, données de test, fichiers temporaires Supprimer la source et reconstruire
Exécutable Motifs d’adresses internes, marqueurs de jetons Identifier l’étape de génération et renouveler les identifiants concernés

Éviter les dérives et les erreurs courantes

La dérive la plus fréquente vient d’un « Scheme présent en local, mais non partagé ». Si le dépôt ne contient pas le xcshareddata correspondant, la CI risque de ne pas trouver le Scheme, ou un ingénieur peut remplacer provisoirement le point d’entrée par un autre. Le deuxième problème consiste à coder le nom de l’environnement dans les branches d’un script sans faire de la Configuration la source unique. Avec le temps, deux logiques de décision distinctes finissent ainsi par coexister.

Une autre erreur consiste à croire qu’une variable devient sûre dès lors qu’elle est obfusquée, fractionnée ou inscrite dans un binaire. Il n’existe aucun moyen fiable de dissimuler durablement un secret dans un client. Si vous découvrez qu’un secret a été intégré à d’anciennes archives, commencez par révoquer ou renouveler les identifiants, puis corrigez la configuration du build. Supprimer la chaîne de la branche actuelle n’élimine pas le risque lié aux versions déjà distribuées.

Lorsque ce processus est exécuté sur les nœuds physiques dédiés de MangoVM, veillez également à ce que le Runner utilise un répertoire de travail propre et ne réutilise ni le DerivedData, ni les configurations temporaires, ni les répertoires d’export d’une tâche précédente. Limitez le nettoyage à l’espace de travail de la tâche courante afin d’éviter que des tâches concurrentes ne suppriment leurs fichiers respectifs.

Transformer les contrôles en barrières de fusion et de publication

Le processus final peut être divisé en deux étapes : lors de la demande de fusion, résoudre les réglages et vérifier la structure des fichiers de configuration ; lors de la publication, produire l’archive complète et inspecter le livrable. La première étape fournit un retour rapide, tandis que la seconde se fonde sur ce qui sera réellement livré. Toute assertion d’environnement qui échoue doit renvoyer un état différent de zéro, et non se limiter à un avertissement dans les journaux.

Avant de valider les changements, utilisez la liste de contrôle suivante :

Lorsque la source de la configuration, les valeurs résolues et le livrable final constituent trois niveaux de preuve, le choix de l’environnement ne dépend plus des habitudes de manipulation. Les erreurs de publication sont détectées avant que l’archive ne quitte la pipeline, et non par les utilisateurs à la place de l’équipe.

Questions fréquentes

Peut-on stocker une clé API secrète dans un fichier xcconfig ?

Non. Une valeur de build peut finir dans Info.plist, les options du compilateur, une ressource ou le binaire. Une archive d’application doit être considérée comme publique ; les secrets privilégiés restent donc côté serveur.

Pourquoi la CI archive-t-elle un autre environnement que le Mac local ?

Le Scheme peut ne pas être partagé, la Configuration peut pointer vers une autre base ou une variable peut n’exister que dans un shell interactif. Passez workspace, scheme et configuration explicitement, puis comparez showBuildSettings.

Nœud physique dédié

Configurez un Mac cloud fixe pour vos pipelines de développement

MangoVM propose des nœuds physiques Apple Silicon en deux versions : M4 et M4 Pro. Choisissez la durée de location, la région et les options de stockage pour vérifier le récapitulatif complet de votre commande.

Choisir une configuration et commander