Détecter les dépendances locales cachées par un clonage propre en CI

DevOps et CI/CD ·~6 min de lecture

Détecter les dépendances locales cachées par un clonage propre en CI

Un même projet Xcode peut compiler dans un répertoire de développement utilisé depuis longtemps, puis signaler des fichiers inexistants, des scripts introuvables ou des valeurs de configuration vides après sa copie dans un nouvel espace de travail Mac cloud VPSGit. Dans ce cas, ne commencez pas par transférer les anciens caches. Il faut d’abord vérifier qu’un build peut s’exécuter uniquement à partir du commit indiqué, d’une chaîne d’outils explicitement déclarée et des paramètres injectés par le pipeline.

Quels états isoler lors d’un clonage propre

Supprimer DerivedData n’écarte qu’une seule catégorie de cache et ne prouve pas que le projet est indépendant de la machine locale. Un espace de travail utilisé depuis longtemps accumule généralement des fichiers non commités, des configurations dans le répertoire utilisateur, des outils installés globalement, des scripts d’initialisation du Shell et des chemins absolus pointant hors du dépôt.

Il faut isoler au minimum les cinq couches suivantes :

L’objectif d’une validation par clonage propre n’est pas de « nettoyer une fois de plus » avant le build, mais de prouver qu’aucun état absent du dépôt, du manifeste des dépendances ou des paramètres du pipeline ne peut être utilisé silencieusement.

Lors du premier diagnostic, conservez un cas de référence : le build réussit dans l’espace de travail d’origine, mais échoue dans l’espace isolé. Les deux doivent utiliser le même commit, le même Scheme, la même Configuration et la même Destination afin que les différences observées soient réellement exploitables.

Créer un espace de travail isolé et temporaire

Le script ci-dessous crée depuis un dépôt existant un clone qui n’utilise pas les liens physiques locaux, extrait le commit indiqué, puis place le répertoire utilisateur, le répertoire temporaire et le cache de build dans un emplacement éphémère. Le nom du projet et le Scheme sont fournis en paramètres afin de ne pas figer les différences d’environnement dans le script.

#!/bin/bash
set -euo pipefail

SOURCE_REPO="${1:?source repository required}"
REVISION="${2:?revision required}"
PROJECT="${3:?project path required}"
SCHEME="${4:?scheme required}"

ROOT="$(mktemp -d "${TMPDIR:-/tmp}/clean-checkout.XXXXXX")"
trap 'rm -rf "$ROOT"' EXIT

git clone --no-local --no-checkout "$SOURCE_REPO" "$ROOT/repo"
git -C "$ROOT/repo" checkout --detach "$REVISION"
git -C "$ROOT/repo" submodule update --init --recursive

mkdir -p "$ROOT/home" "$ROOT/tmp" "$ROOT/DerivedData"
export HOME="$ROOT/home"
export TMPDIR="$ROOT/tmp"
export DEVELOPER_DIR="/Applications/Xcode.app/Contents/Developer"

cd "$ROOT/repo"
xcodebuild \
  -project "$PROJECT" \
  -scheme "$SCHEME" \
  -configuration Debug \
  -derivedDataPath "$ROOT/DerivedData" \
  CODE_SIGNING_ALLOWED=NO \
  build | tee "$ROOT/build.log"

CODE_SIGNING_ALLOWED=NO convient uniquement à la validation des étapes de compilation qui ne nécessitent pas de signature. Si la cible doit être archivée ou signée, les éléments requis doivent être injectés de manière sécurisée par la CI. Ne référencez pas de fichiers stockés dans le répertoire personnel d’un utilisateur simplement pour faire passer le script.

Faire apparaître au plus tôt les dépendances manquantes

Après le clonage, exécutez d’abord git status --porcelain ; la commande ne doit produire aucun résultat. Vérifiez ensuite l’état des sous-modules et la présence de tous les objets de fichiers volumineux. Si le projet comprend une étape de génération de code, distinguez clairement deux catégories d’artefacts : le code source et la version du générateur doivent être verrouillés ; les résultats générés doivent soit être commités conformément aux règles de l’équipe, soit être produits de manière reproductible pendant le build.

N’ajoutez pas ~/bin, /usr/local/bin ni le bureau d’un membre de l’équipe aux chemins de secours. Les outils auxiliaires doivent se trouver dans un répertoire d’outils du dépôt, être déclarés dans un manifeste de gestion de paquets avec une version contrainte, ou être installés dans une version fixe par le processus d’initialisation du nœud.

Identifier les entrées implicites à partir des journaux d’échec

Lorsqu’un build isolé échoue, recherchez la première erreur réelle plutôt que le récapitulatif situé à la fin du journal. Les signaux courants peuvent être classés selon le type d’entrée concerné :

Signal d’échec Cause fréquente Piste de correction
No such file or directory Fichier non commité ou chemin absolu Ajouter le fichier au dépôt et utiliser un chemin relatif au dépôt
command not found Dépendance à un Shell interactif ou à un outil global Déclarer la version de l’outil et définir explicitement PATH
Valeur de configuration vide Paramètre présent uniquement dans la configuration locale L’injecter depuis la CI et échouer immédiatement s’il manque
Module visible dans l’ancien répertoire Un cache partagé masque une dépendance manquante Corriger la déclaration de dépendance au lieu de copier l’ancien cache
Erreur de permission sur un script Le bit exécutable n’a pas été inclus dans le commit Corriger le mode du fichier et créer un nouveau commit

Les commandes suivantes permettent de vérifier si le commit courant contient réellement le fichier attendu et les permissions associées :

git ls-tree -r HEAD -- path/to/file
git status --short
git diff --summary HEAD

Si le chemin en erreur pointe vers /Users/某个名字/, il est généralement enregistré sous forme absolue dans la configuration du projet, un script ou un fichier généré. Corrigez la configuration source qui produit ce chemin au lieu de créer un répertoire du même nom sur le nouveau nœud.

Restreindre l’environnement sans provoquer de faux échecs

Utiliser directement env -i permet de détecter rapidement les dépendances à l’environnement, mais peut aussi supprimer le PATH, les paramètres régionaux et le répertoire temporaire nécessaires au build. Une approche plus fiable consiste à relever d’abord les variables effectivement lues par le build, puis à établir une liste blanche.

Il est recommandé de conserver HOME, TMPDIR, PATH, DEVELOPER_DIR, LANG ainsi que les paramètres métier explicitement injectés par le pipeline. Ajoutez un contrôle obligatoire pour chaque variable personnalisée, par exemple :

: "${API_BASE_URL:?API_BASE_URL must be provided by CI}"
: "${BUILD_FLAVOR:?BUILD_FLAVOR must be provided by CI}"

Une configuration manquante fera ainsi échouer le script dès son démarrage, plutôt que de provoquer une erreur ambiguë vers la fin de la compilation. Pour les valeurs sensibles, vérifiez uniquement leur présence sans les afficher dans les journaux. Le script de build ne doit pas non plus lire la configuration d’un Shell interactif, car ces fichiers peuvent ne jamais être chargés lors d’une tâche non interactive.

Intégrer la validation au pipeline quotidien

Un clone entièrement indépendant augmente l’utilisation du disque et le temps d’extraction ; il n’est donc pas nécessaire de bloquer chaque petit commit avec cette opération. La validation peut être divisée en deux niveaux : les tâches ordinaires réutilisent un espace de travail pour fournir un retour rapide, tandis que la validation par clonage propre s’exécute lors d’une fusion vers la branche principale, d’un changement de dépendance ou de version de Xcode, ainsi qu’avant une publication.

La validation ne doit réussir que si toutes les conditions suivantes sont remplies :

  1. Le commit indiqué peut être extrait intégralement dans un clone indépendant.
  2. L’arbre de travail ne contient aucune modification inattendue avant ou après le build.
  3. Le build ne lit ni l’espace de travail d’origine ni les répertoires personnels des utilisateurs.
  4. Tous les outils auxiliaires proviennent d’une source de version traçable.
  5. Les artefacts attendus sont toujours produits après suppression de DerivedData.
  6. Les journaux d’échec et le résumé de l’environnement permettent de reproduire le problème sans contenir de valeurs sensibles.

Lorsqu’une validation par clonage propre échoue alors qu’un build ordinaire réussit, considérez ce résultat comme un défaut de déclaration des dépendances. Corrigez d’abord les limites des entrées, puis seulement la restauration des caches. Maintenir cette validation dans la durée transforme la prise de relais par de nouveaux nœuds, l’extension parallèle de la capacité et les mises à niveau de la chaîne d’outils en processus d’ingénierie reproductibles, plutôt qu’en essais aléatoires sur une autre machine.

Questions fréquentes

Pourquoi git clean ne suffit-il pas pour cette vérification ?

git clean ne supprime que les fichiers non suivis de l’arborescence courante. Il n’isole ni le HOME, ni les outils globaux, ni les caches partagés, ni le Keychain, ni les chemins externes au dépôt.

Que faut-il examiner en premier après l’échec du build propre ?

Comparez d’abord les chemins d’entrée et les variables d’environnement de la commande en échec, puis vérifiez les sous-modules, fichiers volumineux, générateurs, configurations et outils déclarés.

Faut-il exécuter ce contrôle à chaque commit ?

Ce n’est pas nécessaire. Gardez un contrôle rapide au quotidien et lancez l’isolation complète avant une livraison, après une mise à jour de Xcode ou des dépendances, et lors des fusions majeures.

Nœud physique dédié

Configurez un Mac cloud pour votre prochain build ou votre prochaine tâche d’inférence

Choisissez parmi trois configurations Apple Silicon, six nœuds et des durées de location fixes. La disponibilité réelle est indiquée en temps réel dans la console.

Configurer un Mac cloud