Un pipeline peut n’afficher qu’un seul nombre — 65 — alors que la commande xcodebuild a échoué plusieurs lignes plus tôt. Apple documente les opérations de génération, de test et de conservation des résultats, mais ne présente pas Exit Code 65 comme une cause unique. Ne lancez donc pas une commande de nettoyage au hasard : conservez la commande complète et le fichier xcresult, identifiez le premier échec, puis contrôlez le Scheme, les dépendances, la destination, la signature et enfin le nœud Mac distant.
Qui doit poursuivre la lecture ?
Ce guide s’adresse aux développeurs dont la compilation réussit dans Xcode mais échoue sur un CI Mac distant. Il concerne aussi les ingénieurs DevOps qui administrent un nœud partagé et les responsables de publication qui doivent séparer une erreur de compilation, de test, de signature ou d’archivage.
La chronologie de diagnostic : du code 65 à la première preuve
Votre objectif n’est pas de faire disparaître le code 65. Il est de remonter jusqu’à l’action qui l’a produit. Cette chronologie évite de confondre le message final de la plateforme CI avec l’erreur technique initiale.
- Départ — état brut : conservez l’heure de l’échec, l’utilisateur d’exécution, le chemin de Xcode, la branche ou le commit, la commande exacte et la destination demandée.
- Premier jalon — résultat exploitable : exécutez la même action avec
-resultBundlePathafin de conserver le bundlexcresult. Apple explique comment exploiter les résultats de test et leurs journaux dans sa documentation sur l’interprétation des résultats Xcode. - Deuxième jalon — première rupture : repérez la première erreur qui concerne réellement la compilation, la résolution d’une dépendance, le lancement d’un test ou la signature. Les messages suivants peuvent être de simples conséquences.
- Troisième jalon — reproduction minimale : retirez les actions non indispensables, mais ne changez pas en même temps le Scheme, la destination et le mode de signature.
- Dernier jalon — décision du nœud : ne reconstruisez le Mac que si une commande minimale échoue encore dans un espace de travail propre, après comparaison de l’environnement.
Un journal qui se termine par « command failed with exit code 65 » ne constitue donc pas une preuve sur la cause. Une erreur de certificat, un test impossible à lancer ou un script interrompu peuvent tous être masqués par la même enveloppe CI. Le fichier xcresult permet de distinguer les actions de test, les cibles concernées et les journaux associés, au lieu de travailler sur les dernières lignes du terminal.
Comment séparer le premier échec des erreurs en cascade ?
Commencez par rechercher le premier message contenant un verbe d’action : compilation, résolution, lancement, signature ou archivage. Un résumé tel que « tests failed » est moins utile qu’un message indiquant qu’une destination n’est pas disponible ou qu’une identité de signature est introuvable.
Conservez également la sortie non filtrée. Un filtre qui ne garde que error: peut supprimer le contexte d’un script ou le nom de la cible. La plateforme CI peut ensuite ajouter son propre message d’échec ; celui-ci doit être classé comme information d’orchestration, pas comme diagnostic de Xcode.
Projet local contre commande CI : le premier embranchement
Le premier contraste oppose le projet réellement construit à celui que vous pensez construire. Un poste local peut ouvrir un workspace et un Scheme partagé, tandis que le script distant appelle par erreur un fichier de projet, une configuration ou une destination différente.
Première étape : vérifier le support de compilation
Listez les Schemes visibles depuis le répertoire de travail, puis comparez celui utilisé par le pipeline avec celui que vous lancez localement. Vérifiez les points suivants :
- le fichier
.xcworkspaceou.xcodeprojappelé ; - le nom exact du Scheme ;
- le caractère partagé du Scheme dans le dépôt ;
- l’action exécutée : build, test ou archive ;
- la configuration de compilation ;
- le SDK et la destination ;
- le chemin de sortie et le répertoire de travail.
Pour une vérification contrôlée, utilisez des valeurs fictives dans votre script :
xcodebuild \
-workspace "Projet.xcworkspace" \
-scheme "SchemeExemple" \
-configuration "ConfigurationExemple" \
-destination 'platform=iOS Simulator,id=IDENTIFIANT_SIMULATEUR' \
-resultBundlePath "Artefacts/resultats.xcresult" \
test
Ne copiez pas cette commande sans remplacer les espaces réservés. Le but est de rendre explicites les choix que l’interface graphique peut masquer. Apple documente la syntaxe et les options de ligne de commande dans sa note technique consacrée à xcodebuild.
Pourquoi un réglage passé sur la ligne de commande peut-il contredire le projet ?
Un Build Setting transmis directement à xcodebuild peut prendre le dessus sur la valeur définie dans le projet. Une variable ajoutée par le script, une configuration injectée par la plateforme CI ou un fichier de paramètres différent peut donc modifier le comportement sans changement visible dans Xcode. La hiérarchie des réglages est détaillée dans la documentation Apple sur la configuration des Build Settings.
Affichez les réglages effectifs pour la cible et la configuration concernées. Comparez ensuite cette sortie avec celle du poste local, en particulier pour le SDK, l’architecture, le chemin des produits, la signature et les paramètres de test. Si le résultat diffère, corrigez d’abord le script ou la configuration versionnée. Supprimer DerivedData avant cette comparaison ne ferait que détruire une preuve.
Dépendances contre scripts : résolution avant compilation
Le deuxième embranchement oppose un échec de dépendance ou de script à une erreur du compilateur Swift ou Objective-C. Cette distinction est importante sur un nœud distant, car l’utilisateur du service CI ne possède pas nécessairement les mêmes identifiants SSH, le même répertoire personnel ou le même trousseau que votre session graphique.
Quand Package.resolved devient-il la preuve principale ?
Si le premier échec apparaît pendant la résolution Swift Package, contrôlez le fichier Package.resolved, la révision attendue, l’accès au dépôt privé et les informations d’authentification. Vérifiez également :
- la présence de la clé SSH pour l’utilisateur qui exécute le pipeline ;
- le contenu de
known_hostsde ce même utilisateur ; - les droits de lecture sur le répertoire de travail ;
- la disponibilité du programme Git attendu ;
- l’absence de modification non validée du fichier de résolution.
Apple recommande une gestion reproductible des dépendances dans les workflows d’intégration continue et décrit les limites liées aux outils et aux identifiants dans son guide de construction d’applications utilisant Swift Package.
Ne remplacez pas immédiatement la résolution par une installation forcée. Cette action peut faire disparaître le symptôme tout en introduisant une révision différente. Votre test de rétablissement doit utiliser le même commit, le même compte d’exécution et la même configuration réseau.
Un Run Script Phase peut-il produire Exit Code 65 ?
Oui, si le script termine avec un état d’échec, le pipeline peut remonter un échec global de xcodebuild. Cherchez le premier message du script, son répertoire courant, son interpréteur, ses variables et les fichiers d’entrée attendus. Un fichier généré absent, une commande disponible dans votre session mais pas dans l’environnement CI, ou un chemin relatif incorrect sont des causes plus précises que « Xcode est instable ».
Pour chaque script, enregistrez le répertoire courant et les variables strictement nécessaires. Évitez d’imprimer des secrets. Vérifiez ensuite que les sorties déclarées comme entrées et sorties correspondent aux fichiers effectivement créés. La réparation consiste à rendre le script autonome et déterministe, non à désactiver toutes les vérifications.
Compilation contre test : contrôler la destination Simulator
Un troisième contraste oppose la réussite de la compilation à la possibilité de lancer les tests. Une application peut être correctement compilée alors que la destination demandée n’existe pas, que le runtime requis n’est pas installé ou que la cible de test ne correspond pas à la plateforme sélectionnée.
Pourquoi un Simulator lancé ne garantit-il pas un test fonctionnel ?
Le lancement visuel d’un Simulator ne prouve pas que la chaîne de test est prête. Le Scheme doit inclure la cible correcte, le runtime doit correspondre à la destination, et l’application ainsi que ses dépendances doivent être installées dans le contexte prévu. Un échec au démarrage, à l’installation ou à l’exécution peut donc produire le même code final que certains échecs de compilation.
Dans xcresult, examinez la session de test, la cible, la destination et le journal lié à la première action interrompue. La documentation Apple sur les résultats de test fournit le cadre pour interpréter ces informations.
Procédez dans cet ordre :
- Confirmez que le Simulator demandé existe sur le nœud.
- Confirmez que son runtime est installé et compatible avec le projet.
- Vérifiez que la destination utilisée par le pipeline est exactement celle de la reproduction.
- Lancez uniquement la cible de test concernée.
- Répétez le test avec le même identifiant de destination après correction.
Ne réinitialisez pas tous les Simulators par réflexe. Une réinitialisation détruit l’état utile au diagnostic et peut modifier le résultat d’un test dépendant de données préparées. Si un nettoyage devient nécessaire, documentez l’état initial et limitez l’opération à la destination concernée.
Signature contre archivage : séparer les phases de publication
La signature doit être inspectée uniquement lorsque le premier échec pointe vers une identité, une équipe, une clé privée, un profil ou un trousseau. Une compilation locale réussie avec signature automatique ne garantit pas que l’utilisateur sans interface graphique du CI voit les mêmes identités numériques.
Deuxième étape : identifier le compte qui possède réellement les droits
Sur le Mac distant, relevez le compte qui lance xcodebuild, puis comparez-le avec celui utilisé pendant votre session graphique. Vérifiez l’accès à la clé privée, au trousseau déverrouillé, au certificat et au profil d’approvisionnement. Contrôlez également l’équipe et les paramètres de signature réellement transmis par la commande.
Les réglages de signature ne doivent pas être remplacés par une désactivation générale pour obtenir un résultat vert. Cette méthode peut permettre une compilation de développement, mais elle ne valide pas une archive destinée à la distribution. La documentation Apple sur la construction avec les informations de débogage aide à distinguer le produit compilé et les artefacts nécessaires à son analyse ; elle ne transforme pas un problème d’identité en problème de cache.
Validez séparément :
- la compilation sans archive ;
- l’archivage ;
- l’export de l’archive ;
- la présence des artefacts attendus.
Conservez un résultat xcresult ou un journal propre pour chaque phase. Si l’archive réussit mais que l’export échoue, ne revenez pas au diagnostic de compilation : la frontière de responsabilité est déjà identifiée.
Environnement du Mac distant : corriger, isoler ou reconstruire
Le dernier contraste oppose un projet défectueux à un nœud qui ne reproduit plus un état stable. Contrôlez le chemin de Xcode sélectionné, la variable DEVELOPER_DIR, l’utilisateur, les permissions, l’espace disque, le répertoire de travail résiduel et la différence entre une session après redémarrage et une session déjà ouverte.
Apple décrit aussi la configuration des outils en ligne de commande dans sa documentation dédiée à leur sélection. Cette vérification est essentielle lorsque plusieurs installations ou composants de développement coexistent sur le même Mac.
Faut-il effacer DerivedData ou reconstruire le nœud ?
La réponse dépend de la preuve, pas de la fréquence du code 65.
- Si le même commit échoue avec le même premier message sur plusieurs espaces de travail propres, corrigez le projet, la dépendance, la destination ou la signature.
- Si seul le répertoire de travail existant échoue, sauvegardez les journaux, nettoyez uniquement ce répertoire, puis relancez la commande identique.
- Si deux utilisateurs obtiennent des résultats différents, isolez les permissions, le trousseau, les variables et le contexte de session avant toute reconstruction.
- Si la commande minimale réussit sur un autre nœud mais échoue après redémarrage sur le premier, placez le nœud en quarantaine et cherchez une dépendance à l’état de session ou à un composant non persistant.
- Si un environnement propre, le même compte et la même commande échouent encore, reconstruisez seulement après avoir conservé les preuves et vérifié que le projet n’est pas la cause.
Cette méthode répond aussi au choix entre réparation et remplacement d’un Mac distant. La reconstruction est une mesure de restauration, pas un diagnostic. Elle peut masquer une configuration de projet incorrecte et provoquer la répétition de la panne sur le nœud suivant.
Outil de décision : quelle action choisir après le premier échec ?
Utilisez cette liste de contrôle à cocher après avoir enregistré la commande, le journal complet et le fichier xcresult. Ne cochez qu’une branche principale à la fois, afin de ne pas modifier plusieurs variables pendant la même expérience.
-
[ ] Le premier message cite le workspace, le Scheme, la configuration, le SDK ou la destination.
Choisissez la correction des paramètres CI. Comparez la commande distante avec la commande locale, puis relancez exactement la même action avec le réglage corrigé. -
[ ] La résolution de
Package.resolvedou l’accès à un dépôt privé échoue avant la compilation.
Choisissez la correction des dépendances et des identifiants du compte CI. Ne supprimez pas le cache avant d’avoir prouvé que la révision et l’accès au dépôt sont corrects. -
[ ] Un
Run Script Phaseest le premier élément en échec.
Choisissez la correction du script. Vérifiez son répertoire, son shell, ses variables et ses fichiers d’entrée avant de modifier Xcode ou le nœud. -
[ ] La compilation réussit, mais le Simulator, l’installation ou la cible de test échoue.
Choisissez la correction de la destination et du runtime. Répétez le test avec le même identifiant de Simulator, sans réinitialiser toutes les destinations. -
[ ] Le premier échec concerne le certificat, le profil, le trousseau ou l’équipe.
Choisissez la réparation de la signature sous le compte CI. Validez séparément build, archive et export ; ne désactivez pas la signature pour un pipeline de publication. -
[ ] La commande minimale échoue dans un espace de travail propre, tandis qu’un autre nœud réussit avec le même commit et le même compte.
Choisissez l’isolement du nœud. Comparez Xcode,DEVELOPER_DIR, les permissions, l’espace disque et l’état après redémarrage. -
[ ] Les résultats restent instables après cette comparaison.
Choisissez la reconstruction ou le remplacement du nœud, mais seulement après archivage des preuves. Sinon, vous risquez de déplacer le même défaut vers une nouvelle machine.
Cette liste répond à la question du nettoyage de DerivedData : il ne s’agit pas d’un remède universel. Nettoyez un espace de travail précis uniquement lorsque les indices montrent un résidu local, et conservez son état avant l’opération. Pour une panne de signature ou de destination, ce nettoyage n’attaque pas la cause démontrée.
La matrice de rétablissement : quatre résultats à conserver
Avant de déclarer le CI rétabli, conservez les quatre résultats suivants avec leur commande complète et leur xcresult :
- la commande originale en échec ;
- la commande minimale dans un espace de travail propre ;
- la même commande après la correction ;
- la même commande après redémarrage du nœud.
Interprétez-les ainsi :
- Si le premier échec reste identique dans les quatre résultats : corrigez le projet ou la chaîne de signature.
- Si seul le premier résultat échoue : recherchez un résidu dans l’espace de travail ou une différence de paramètres.
- Si le troisième réussit mais que le quatrième échoue : traitez le nœud comme non fiable et isolez-le.
- Si le quatrième réussit avec le même commit, la même destination et le même compte : réintégrez le nœud, puis surveillez une nouvelle exécution indépendante.
Le test après redémarrage est important pour un CI Mac distant : une session graphique ouverte, un trousseau temporairement déverrouillé ou un processus Simulator déjà actif peuvent rendre une réparation illusoire. Pour les tests automatisés via SSH, Apple fournit également des indications sur les sessions de test et l’automatisation à distance.
Ce que votre choix d’infrastructure doit changer
Si vous utilisez actuellement un poste local partagé, une machine virtuelle ou un serveur non spécialisé, vous rencontrez souvent quatre limites concrètes : l’état du compte n’est pas reproductible, l’accès aux certificats varie selon la session, le redémarrage interrompt les tâches longues et la destination Simulator n’est pas garantie après une maintenance. Un serveur distant générique peut en outre ne pas fournir l’accès complet à macOS, à Xcode ou au matériel Apple nécessaire à votre chaîne.
Après la reproduction minimale, une machine Mac distante administrable avec des droits complets permet de reprendre exactement le même dépôt, la même commande et les mêmes contrôles d’environnement. VMSPIN peut alors servir de poste de validation temporaire avant de choisir une location plus longue : consultez d’abord les possibilités de Mac distant pour vos environnements de développement, puis comparez la durée adaptée à votre cycle dans les options de location Mac.
Cette approche ne rend pas la location préférable dans tous les cas. Un projet qui exige une charge constante pendant une longue période peut justifier l’achat et l’administration d’un Mac dédié. De même, un besoin d’interface physique, de périphérique propriétaire ou de contrôle local permanent impose de vérifier les limites d’un accès distant. En revanche, pour isoler un doute de nœud, tester une archive, valider une destination ou maintenir temporairement un CI Mac, louer un Mac VMSPIN évite de modifier votre poste principal avant d’avoir établi la cause.
Le bon ordre reste donc inchangé : capturer le journal complet et xcresult, prouver le premier échec, corriger la couche concernée, puis répéter la commande après redémarrage. Si l’erreur disparaît sur un Mac distant propre, vous pourrez évaluer sereinement la durée de location et l’isolation du nœud ; si elle persiste, le code 65 vous aura au moins conduit vers la véritable ligne fautive plutôt que vers un nettoyage aveugle.