Un diagnostic sérieux doit séparer quatre actions xcodebuild — Build, Test, Archive et exportArchive — avant de chercher une correction, comme le montrent les flux de travail et les étapes de test décrits par la documentation Apple sur l’exécution des tests et les actions de workflow Xcode Cloud. La conclusion est immédiatement exploitable : xcodebuild exit code 65 est un état d’échec global, pas une cause précise. Il faut conserver le journal complet et le fichier xcresult, identifier l’étape fautive, puis seulement modifier le projet ou l’environnement du Mac distant.
Cette méthode concerne les développeurs indépendants dont l’application se construit dans Xcode, mais échoue depuis SSH ou une intégration continue. Elle convient aussi aux petites équipes qui exécutent des tests sur simulateur ou préparent une archive signée sans interface graphique.
[ SECTION_01 ] Commencer par préserver la preuve de l’échec
Lorsque le journal se termine par xcodebuild: error: ... puis exit code 65, la dernière ligne est rarement le meilleur indice. Un échec de compilation, de test ou de signature peut avoir été signalé bien plus tôt. Le premier réflexe ne doit donc pas être de supprimer les caches, mais de rendre l’échec reproductible et lisible.
Une exécution distante doit conserver les éléments suivants :
- la commande complète, avec les chemins sensibles remplacés par des valeurs neutres ;
- le projet ou le workspace utilisé ;
- le Scheme et la Configuration réellement sélectionnés ;
- la Destination et le SDK demandés ;
- l’active developer directory ;
- la révision exacte du dépôt ;
- la sortie standard et la sortie d’erreur ;
- le fichier
xcresult, lorsqu’une action de test ou de construction le produit.
Le fichier xcresult ne remplace pas le journal texte. Il complète celui-ci avec les résultats structurés, les diagnostics et les informations associées à l’action. Les chemins, noms de projet, identifiants d’appareil, comptes, Bundle ID, Team ID et noms de certificats doivent être anonymisés avant archivage ou partage.
Une conservation minimale peut ressembler à ceci, avec des valeurs fictives :
set -o pipefail
xcodebuild \
-workspace App.xcworkspace \
-scheme App \
-configuration Release \
-destination 'generic/platform=iOS' \
-resultBundlePath artefacts/build.xcresult \
build 2>&1 | tee artefacts/build.log
code=${PIPESTATUS[0]}
printf '%s\n' "$code" > artefacts/exit-code.txt
exit "$code"
Le point important n’est pas la syntaxe décorative. La tâche doit transmettre le véritable code de sortie tout en conservant les preuves. Sans pipefail et sans récupération correcte du code du processus xcodebuild, un script peut masquer l’échec ou retourner le statut de tee au lieu de celui de la construction.
[ SECTION_02 ] Le projet, le Scheme et la Configuration doivent désigner la même chose
Une cause fréquente de divergence est l’utilisation d’un projet alors que les dépendances sont intégrées dans un workspace. Une commande qui fonctionne dans Xcode peut donc échouer lorsqu’un script appelle directement le fichier .xcodeproj.
Le contrôle doit suivre cette séquence :
- identifier si l’interface ouvre un
.xcworkspaceou un.xcodeproj; - reprendre le même point d’entrée dans la commande distante ;
- vérifier que le Scheme existe dans ce conteneur ;
- confirmer que le Scheme est partagé et versionné avec le dépôt ;
- comparer la Configuration utilisée en local et à distance ;
- vérifier le SDK et la Destination effectivement résolus.
Les Schemes déterminent les actions et les réglages transmis à la construction. Apple décrit leur personnalisation dans la documentation consacrée aux Build Schemes. Le problème n’est donc pas simplement « le Scheme est visible dans Xcode ». Il faut démontrer qu’il est visible pour le même compte, depuis le même répertoire et avec la même révision.
Pour comparer les contextes, la commande doit être exécutée dans un terminal interactif puis dans la session automatisée, sans modifier le dépôt entre les deux essais :
xcodebuild -list -workspace App.xcworkspace
xcode-select -p
pwd
env | sort
Les valeurs confidentielles doivent être filtrées avant conservation. Une différence de pwd, de xcode-select, de variables ou de fichier de configuration peut expliquer pourquoi Xcode construit l’application alors que xcodebuild renvoie exit code 65.
[ SECTION_03 ] La chaîne de dépendances et les scripts ne se réparent pas avec un nettoyage aveugle
Un code de sortie identique peut recouvrir plusieurs pannes : résolution de paquets, compilation Swift ou Objective-C, traitement de ressources, édition des liens ou script de construction. Ces phases ne se corrigent pas de la même manière.
Le diagnostic doit classer le premier message utile :
| Premier signal dans le journal | Couche probable | Vérification prioritaire | Correction prudente |
|---|---|---|---|
| Paquet introuvable ou résolution refusée | Dépendances | fichier de verrouillage, accès au dépôt privé, révision | rétablir l’accès et reconstruire avec la même révision |
| Symbole ou module absent | Compilation ou édition des liens | SDK, architecture, réglages de cible | corriger la configuration de la cible |
Échec dans Run Script |
Script de construction | interpréteur, droits, répertoire courant, variables | exécuter le script dans un environnement contrôlé |
| Ressource ou fichier généré absent | Préparation des ressources | ordre des phases, outil générateur, chemin de sortie | rendre la génération explicite et vérifiable |
Le fichier de verrouillage doit être traité comme une entrée du build, non comme un détail local. Une authentification disponible dans le trousseau de la session graphique peut manquer dans SSH. De même, un script qui appelle python, ruby, node ou un outil installé uniquement dans le profil interactif peut échouer en tâche de fond.
Les fichiers de configuration de build méritent un contrôle indépendant. Apple explique comment ajouter un fichier de configuration de build. Il faut vérifier que le fichier est bien présent dans le dépôt, référencé par la cible et accessible depuis le répertoire de lancement.
Après une correction, le test fiable se fait avec une copie propre du dépôt et la même révision. Un build réussi dans un répertoire déjà modifié ne prouve pas que la correction est reproductible. Le nettoyage de Derived Data peut servir de test ciblé, mais il ne doit pas devenir la réponse automatique à chaque exit code 65 : il efface une preuve sans corriger une dépendance absente, un mauvais Scheme ou un secret inaccessible.
[ SECTION_04 ] Simulateur et tests : séparer la construction du lancement
Un test peut échouer avant même l’exécution de l’application. La Destination peut être invalide, le système demandé peut ne pas être installé, le simulateur peut ne pas démarrer ou l’application peut dépasser le délai de lancement. Dans chacun de ces cas, le code final peut rester exit code 65.
La séparation entre construction et exécution réduit fortement l’espace de recherche :
xcodebuild \
-workspace App.xcworkspace \
-scheme App \
-destination 'platform=iOS Simulator,id=SIMULATOR_ID' \
-resultBundlePath artefacts/build-for-testing.xcresult \
build-for-testing
xcodebuild \
-workspace App.xcworkspace \
-scheme App \
-destination 'platform=iOS Simulator,id=SIMULATOR_ID' \
-resultBundlePath artefacts/test-without-building.xcresult \
test-without-building
L’identifiant de simulateur présenté ici est volontairement fictif. Il ne faut pas le copier sans vérifier les appareils et systèmes présents sur l’hôte distant. La première action répond à la question « le produit de test peut-il être construit ? ». La seconde répond à la question « le test peut-il s’installer, démarrer et s’exécuter sur cette Destination ? ».
Apple recommande d’examiner les résultats de test et leurs diagnostics plutôt que de se limiter au statut final, comme l’expose son guide d’interprétation des résultats. Les variables d’environnement peuvent également modifier le comportement des tests ; leur liste de référence figure dans la documentation Apple dédiée.
Attention : une session SSH sans contexte graphique n’est pas équivalente à une session ouverte localement. Avant de conclure à un défaut du code, il faut vérifier l’état du service de simulation, la disponibilité de la Destination et les droits du compte qui lance réellement la tâche.
| Résultat de la séparation | Interprétation | Prochaine action |
|---|---|---|
build-for-testing échoue |
compilation, dépendance, ressource ou script | revenir au premier diagnostic de construction |
build-for-testing réussit, test-without-building échoue |
simulateur, installation, démarrage ou test | inspecter la Destination et le résultat de test |
| Test local réussi, test SSH échoué | différence de session ou de variables | comparer utilisateur, répertoire, services et environnement |
| Même échec après réinitialisation du simulateur | cause probablement hors cache | vérifier projet, système installé ou hôte distant |
[ SECTION_05 ] Archive, signature et export doivent rester des états distincts
Une archive réussie ne signifie pas nécessairement qu’un paquet distribuable est prêt. Il faut distinguer la compilation, la création de l’archive, la signature et l’export. Cette séparation évite de supprimer un certificat alors que l’échec se situe dans un script ou dans le choix de la Configuration.
| État observé | Ce qui est démontré | Ce qui reste à prouver |
|---|---|---|
| Build réussi | le code et les ressources ont été construits selon les réglages choisis | archive et signature |
| Archive créée | un fichier d’archive a été généré | identité, profil et export |
| Signature appliquée | les éléments concernés ont été signés | compatibilité avec la distribution visée |
| Export réussi | un paquet de distribution a été produit | validation et téléversement séparés |
Pour une tâche d’Archive, le journal doit préciser le chemin de sortie de l’archive. Pour l’export, le fichier d’options ne doit pas être supposé identique entre développement, ad hoc et distribution. Apple documente les particularités du processus dans sa note sur la personnalisation de l’Archive et décrit la création de code signé pour la distribution macOS dans sa documentation de signature.
Côté trousseau, trois contrôles sont indispensables :
- l’identité de signature existe réellement ;
- la clé privée associée est présente ;
- le compte automatisé peut utiliser cette identité sans dialogue interactif bloqué.
Un certificat sans clé privée ne suffit pas. Un profil présent dans le mauvais emplacement ne suffit pas non plus. Les profils doivent correspondre à la cible et au mode de distribution. La note technique TN3125 sur les profils de provisioning détaille la relation entre profil et signature.
La synchronisation des identités doit être contrôlée avec prudence. Apple fournit des indications sur le partage des certificats de signature d’une équipe. Toute modification du trousseau, suppression de certificat ou remplacement de profil doit comporter un plan de retour : quelles tâches seront interrompues, quelle identité reste disponible et comment restaurer l’ancien état.
[ SECTION_06 ] La checklist d’acceptation distingue le projet de l’hôte distant
Une correction est crédible seulement si elle survit à une exécution propre. La checklist suivante permet de décider si le projet doit être modifié ou si le Mac distant doit être remis en état.
- [ ] La révision du dépôt est enregistrée avec chaque journal.
- [ ] Le workspace ou le projet utilisé par Xcode est identique à celui de la commande.
- [ ] Le Scheme est partagé, versionné et visible dans la session automatisée.
- [ ] La Configuration, le SDK et la Destination sont consignés.
- [ ] Les fichiers de verrouillage et de configuration sont présents dans une copie propre.
- [ ] Les accès aux dépendances privées sont testés avec le compte réel de la tâche.
- [ ] Chaque
Run Scriptutilise un interpréteur et des chemins disponibles hors session interactive. - [ ]
build-for-testingettest-without-buildingont été exécutés séparément lorsque des tests sont concernés. - [ ] Le journal texte et le fichier
xcresultsont conservés après l’échec. - [ ] L’archive, la signature et l’export sont validés comme trois résultats différents.
- [ ] Le trousseau contient l’identité et la clé privée attendues.
- [ ] Une exécution après reconnexion de la session confirme que le résultat ne dépend pas d’un état graphique temporaire.
Le classement final peut se faire avec cette carte de décision :
| Observation reproductible | Décision | Justification |
|---|---|---|
| L’échec suit toujours la même révision, sur plusieurs sessions | corriger le projet | le défaut suit les entrées du dépôt |
| L’échec n’apparaît qu’avec un compte ou une session | corriger l’environnement | le contexte d’exécution est déterminant |
| L’échec n’apparaît que sur un hôte | reconstruire ou isoler l’environnement | l’état local de l’hôte n’est pas fiable |
| Le résultat varie après reconnexion ou redémarrage | stabiliser l’hôte avant toute modification du code | la session ou les services ne sont pas maîtrisés |
Un Mac distant destiné à la construction doit donc être évalué comme un environnement reproductible, et non comme un simple ordinateur accessible à distance. Les variables persistantes, les droits du compte de service, le stockage des artefacts et l’état des simulateurs doivent être documentés. Pour une équipe qui prépare aussi des contenus audio, vidéo ou des applications de design nécessitant des outils macOS spécialisés, cette discipline évite de mélanger les dépendances créatives avec celles du pipeline de publication.
[ SECTION_07 ] FAQ : interpréter exit code 65 sans mauvaise correction
La réponse courte est toujours la même : le code final indique un échec, mais le journal et le fichier xcresult indiquent où il s’est produit. Une compilation interactive réussie ne valide pas automatiquement le contexte SSH, le Scheme partagé, la Destination ni les secrets de signature.
Les équipes qui souhaitent formaliser cet environnement peuvent consulter le guide sur l’acceptation d’un environnement de construction Mac sans surveillance, puis comparer une exécution locale et une exécution sur un Mac distant avec la même révision.
[ SECTION_08 ] Choisir entre réparer l’hôte et utiliser un Mac distant isolé
Une station locale ou un serveur déjà partagé peut sembler suffisant, mais plusieurs défauts deviennent coûteux : état de trousseau difficile à auditer, simulateurs modifiés par d’autres tâches, dépendances installées dans un profil personnel et absence de preuves après un redémarrage. Une machine virtuelle non macOS ne fournit pas non plus l’environnement nécessaire à Xcode et aux outils Apple.
Après le diagnostic du projet, un essai isolé sur un Mac distant permet de comparer exactement la même révision, la même action et les mêmes artefacts. Si l’échec disparaît, l’ancien hôte est le suspect principal. Si l’échec suit le dépôt, la location ne remplace pas la correction du code ou de la configuration.
Pour un besoin temporaire, un pipeline de validation ou une machine de signature maintenue à distance, les options de Mac disponibles chez NOVAKVM peuvent servir d’environnement de comparaison sans achat immédiat de matériel. Cette approche est moins pertinente pour une charge lourde permanente nécessitant un contrôle physique des périphériques ou une maîtrise complète de l’infrastructure. Elle devient en revanche cohérente lorsque le besoin est de tester, archiver ou maintenir une chaîne iOS stable sans consacrer une machine locale à cette seule fonction.
Questions fréquentes
Pourquoi Xcode réussit-il alors que xcodebuild renvoie exit code 65 ?
Xcode et xcodebuild peuvent ne pas utiliser le même Scheme, la même Configuration, le même répertoire de travail ou le même contexte utilisateur. L’interface graphique conserve aussi un environnement de session différent de SSH. Comparez la commande complète, l’active developer directory, les variables et la Destination, puis examinez le premier message d’erreur dans le fichier xcresult.
Exit code 65 indique-t-il une erreur de signature ou de compilation ?
Non. Ce code indique seulement que l’action xcodebuild n’a pas abouti. La cause peut apparaître pendant la résolution des dépendances, la compilation, l’édition des liens, un script, le lancement d’un test, l’archive ou l’export signé. La signature ne doit être retenue qu’après vérification d’un message explicite concernant l’identité, le profil ou le trousseau.
Que faire si xcodebuild ne trouve pas le Scheme sur un Mac distant ?
Vérifiez d’abord que la commande pointe vers le bon workspace ou projet. Listez les Schemes avec le même compte et depuis le même répertoire que la tâche distante, puis contrôlez que le Scheme est partagé et bien présent dans le dépôt. Une différence entre session graphique et SSH peut également révéler un mauvais chemin, une autre révision ou une Configuration absente.
Comment conserver le journal complet et le fichier xcresult dans une intégration continue ?
Redirigez la sortie standard et la sortie d’erreur vers un fichier horodaté, tout en demandant à xcodebuild d’enregistrer le résultat dans un chemin d’artefact persistant. Conservez la commande, la révision, la Destination et l’active developer directory avec le fichier xcresult. L’artefact doit rester accessible après l’échec, et non seulement dans le répertoire de travail temporaire.