Diagnostiquer les échecs de build et de signature avec le journal unifié macOS
Un build automatisé échoue soudainement en pleine nuit, mais le terminal n’affiche que Command PhaseScriptExecution failed. Une nouvelle tentative réussit pourtant sans problème. Dans ce cas, examiner encore et encore la dernière ligne ne sert à rien. Sur un Mac dans le cloud, les outils de build, les services de signature, les processus réseau et les mécanismes d’autorisation système consignent chacun leurs événements dans le journal unifié de macOS. En les replaçant sur une même chronologie, il est généralement possible de déterminer si l’incident vient d’un script du projet, d’un service système ou d’une interruption de la connexion distante.
Délimiter d’abord l’incident et les conditions de reproduction
Commencez par noter l’heure du nœud, l’heure de lancement de la commande, l’heure de l’échec et le code de sortie. L’heure locale affichée par le client distant peut différer du fuseau horaire du nœud. Utilisez donc systématiquement l’heure du nœud pendant le diagnostic.
date -u '+%Y-%m-%dT%H:%M:%SZ'
set -o pipefail
xcodebuild \
-workspace Demo.xcworkspace \
-scheme Demo \
-configuration Release \
build 2>&1 | tee "$HOME/build-output.log"
printf 'exit=%s
' "${PIPESTATUS[0]}"
Ne supprimez pas immédiatement DerivedData, ne redémarrez aucun processus et n’écrasez pas les journaux d’origine. Copiez d’abord la sortie du build, puis vérifiez si l’échec est reproductible de manière fiable. Pour un problème intermittent, consignez au moins deux exécutions et précisez si elles utilisaient le même commit, le même chemin Xcode, le même environnement Shell et le même répertoire de travail.
Le journal unifié ne remplace pas la sortie du terminal. Le premier explique ce qui s’est passé au niveau du système et des services, tandis que la seconde indique jusqu’à quelle étape la commande de build est allée. Les deux sources doivent couvrir la même fenêtre temporelle.
Réduire le périmètre du journal unifié avec des prédicats
log show permet d’examiner des événements déjà survenus. N’exportez pas d’emblée plusieurs heures de journaux complets : cela produit beaucoup de bruit et alourdit le travail d’expurgation. Délimitez d’abord une fenêtre de dix minutes avant et après l’échec, puis filtrez par nom de processus :
log show \
--last 20m \
--style compact \
--info \
--debug \
--predicate 'process == "xcodebuild" OR process == "codesign"'
Si le terminal ne signale qu’un échec de signature, recherchez ensuite les refus renvoyés par les services de sécurité. Si la session distante a pu perturber le build, examinez sshd ainsi que le processus qui exécute réellement le script. Dans les prédicats, privilégiez les champs process, subsystem, category et eventMessage plutôt qu’une recherche trop large dans l’ensemble du texte.
log show --last 15m --style compact \
--predicate '(process == "sshd") OR (eventMessage CONTAINS[c] "denied")'
Le tableau suivant aide à interpréter les signaux courants, mais toute conclusion doit rester fondée sur le code de sortie et les étapes de reproduction.
| Signal dans le journal | Vérifications prioritaires | Conclusion à ne pas tirer immédiatement |
|---|---|---|
permission denied |
Droits sur les fichiers, accès au trousseau, compte d’exécution | Défaillance matérielle du nœud |
| Processus arrêté | Pression mémoire, processus parent, règles de délai d’expiration des scripts | Installation de Xcode endommagée |
| Session SSH interrompue | Réseau du client, paramètres de maintien de connexion, mode d’hébergement de la tâche | Échec certain du build |
| Aucun élément correspondant dans le service de signature | Trousseau, nom du certificat, association des profils | Nécessité de réinstaller toute la chaîne d’outils |
Observer les événements clés en temps réel pendant la reproduction
Si l’incident est reproductible de manière fiable, ouvrez une seconde session SSH et lancez log stream. Le flux en temps réel sert uniquement à l’observation. N’utilisez pas de critères trop larges, au risque de voir les événements importants noyés dans une sortie continue.
log stream \
--style compact \
--level info \
--predicate 'process == "xcodebuild" OR process == "codesign" OR process == "securityd"'
Lancez ensuite le build dans la session d’origine et mesurez l’intervalle entre le premier événement anormal et l’échec du build. Si une erreur de signature apparaît en premier avant d’être résumée par l’outil de build sous la forme d’un échec générique, traitez en priorité la chaîne de signature. Si la connexion SSH est interrompue alors que le processus de build continue de s’exécuter, vérifiez si la tâche dépend d’un terminal interactif au lieu de conclure à tort à une erreur de compilation.
Ajouter aux scripts un marqueur de corrélation
Vos propres scripts peuvent écrire dans le journal unifié au début et à la fin de leur exécution, avec un subsystem fixe et un identifiant de tâche unique pour chaque lancement. Il n’est alors plus nécessaire de s’appuyer sur une recherche approximative par nom de fichier.
job_id="$(date -u '+%Y%m%dT%H%M%SZ')"
logger -p user.notice -t minid-build "job=$job_id phase=start"
./scripts/build.sh
status=$?
logger -p user.notice -t minid-build "job=$job_id phase=end status=$status"
exit "$status"
L’étiquette écrite par logger peut être retrouvée à l’aide du champ process ou du contenu du message. L’identifiant de tâche ne doit servir qu’à relier les événements d’une même exécution. Il ne doit contenir ni jeton, ni identifiant d’accès au dépôt, ni donnée client.
Exporter des éléments vérifiables et partageables en toute sécurité
La sortie textuelle en ligne de commande convient à une analyse rapide, mais les problèmes complexes nécessitent de conserver une archive .logarchive. Avant l’archivage, vérifiez une nouvelle fois la période couverte afin d’éviter de collecter des données historiques sans rapport avec l’incident.
mkdir -p "$HOME/diagnostics"
sudo log collect \
--last 15m \
--output "$HOME/diagnostics/build-incident.logarchive"
cp "$HOME/build-output.log" "$HOME/diagnostics/"
N’envoyez pas directement tout le répertoire. Recherchez d’abord dans le journal de build les variables d’environnement développées, les chemins absolus du projet, l’adresse du nœud, l’adresse du dépôt, les noms de certificats et les paramètres des scripts. Lors du remplacement des valeurs sensibles, conservez une correspondance stable : remplacez par exemple toujours un même nom d’utilisateur par <USER>. Sans cette cohérence, il devient impossible de savoir si plusieurs événements concernent le même compte.
Ajoutez également une note en texte brut indiquant l’heure UTC, la commande exécutée, le code de sortie, le résultat attendu, le résultat observé, la reproductibilité de l’incident et le commit de la dernière exécution réussie avant l’échec. Les journaux établissent ce qui s’est produit ; la note délimite le périmètre de l’enquête.
Choisir l’action suivante en fonction des preuves
Une fois les données collectées, classez d’abord le problème dans l’une des quatre catégories suivantes : projet, environnement, connexion ou ressources. Pour un problème lié au projet, testez le même commit dans un répertoire de travail propre. Pour un problème d’environnement, vérifiez la sélection de Xcode, l’initialisation du Shell et la visibilité du trousseau. Pour un problème de connexion, distinguez l’interruption de la session de l’arrêt de la tâche. Pour un problème de ressources, contrôlez l’espace disque disponible, la pression mémoire et les processus concurrents.
Avant de clore le diagnostic, effectuez une validation minimale : lancez deux builds consécutifs du même commit et vérifiez que leur code de sortie est nul ; validez les artefacts signés avec codesign --verify --deep --strict ; confirmez que les tâches en arrière-plan se terminent comme prévu après la déconnexion SSH ; vérifiez enfin qu’aucun jeton en clair ne subsiste dans le répertoire de diagnostic. L’incident ne peut être considéré comme entièrement résolu que si toutes ces conditions sont réunies.
Questions fréquentes
Pourquoi la sortie de xcodebuild ne suffit-elle pas toujours ?
Elle décrit surtout les étapes du build. Les refus d’autorisation, services de signature, déconnexions réseau et erreurs système peuvent n’apparaître que dans le journal unifié.
Que faut-il retirer avant de transmettre les journaux ?
Il faut masquer les noms d’utilisateur, chemins absolus, adresses de nœud, URL de dépôt, jetons, noms de certificats et toute donnée métier identifiable.
MiniD Cloud Mac
Louez un Mac mini physique dédié à la journée, à la semaine ou au mois
Chaque offre repose sur un Mac mini physique dédié, accessible en bureau à distance et en SSH ; les modèles, régions et durées disponibles figurent sur la page de commande.