Symptôme : la commande de compilation se termine correctement, puis l’étape d’archivage signale que le binaire, le rapport de test ou l’archive est introuvable.

Correctif le plus rapide : ne devinez plus les sous-dossiers de .build ; exécutez swift build --show-bin-path avec les mêmes paramètres que la compilation, puis alignez la collecte d’artefacts et l’identité du cache sur ce chemin réel.

Cet article s’adresse aux personnes qui maintiennent des scripts Shell, Fastlane ou CI après une mise à niveau vers SwiftPM 6.4. Il concerne aussi les auteurs de Swift Package et de plugins, ainsi que les ingénieurs DevOps responsables de nœuds Mac distants, de caches et de procédures de retour arrière.

Dernière mise à jour : 27 août 2026. Les informations de comportement et de statut sont vérifiées dans les documents SwiftPM, Swift Build, Swift Evolution et Apple Developer référencés ci-dessous. Le statut de Swift 6.4 et celui de Xcode 27 doivent être revérifiés lors d’une nouvelle version Beta, RC ou finale.

Diagnostic initial du pipeline

La première distinction à faire est entre trois pannes qui se ressemblent dans les journaux :

  • la compilation échoue réellement ;
  • la compilation réussit, mais le script reconstruit un mauvais chemin ;
  • la commande s’exécute dans un autre répertoire de travail que celui utilisé par la phase de collecte.
Le signal le plus utile est une commande de build qui retourne un état de sortie positif, suivie d’un message tel que « fichier inexistant » au moment de copier ou téléverser le résultat. Dans ce cas, relancer immédiatement la compilation ne prouve rien : vous risquez seulement de réutiliser le même cache et la même hypothèse erronée.

Depuis le changement documenté du système de build par défaut, l’ancien modèle mental « le binaire se trouve toujours à tel emplacement sous .build » ne constitue plus une interface CI fiable. La documentation officielle de migration vers Swift Build recommande précisément d’interroger le chemin de sortie plutôt que de dépendre d’une arborescence interne.

Conservez donc, dans un artefact de diagnostic, les éléments suivants :

  1. la commande exacte, avec toutes ses options ;
  2. le répertoire de travail absolu ;
  3. la version de Swift et l’état de xcode-select ;
  4. l’architecture, la configuration et la destination ;
  5. la sortie complète de swift build --show-bin-path ;
  6. le chemin effectivement utilisé pour la copie ;
  7. l’état de sortie de chaque étape, et non seulement celui du pipeline final.
Cette trace permet de séparer un changement de comportement Swift Build d’un problème de permissions sur un Mac distant, d’un cache contaminé ou d’un simple cd manquant.

Grille de décision avant modification

Ne commencez pas par remplacer toutes les commandes du pipeline. Identifiez d’abord le rôle de chaque chemin et la personne qui le consomme. Un binaire destiné à l’exécution, une ressource de plugin et un rapport de test ne doivent pas être traités comme s’ils provenaient nécessairement du même répertoire.

<
Élément observéHypothèse risquéeVérification recommandéeScore de confiance
Binaire exécutableSous-dossier .build fixeswift build --show-bin-path avec les paramètres du build5/5
Ressource ou sortie de pluginChemin concaténé dans le pluginInspecter les entrées, sorties et journaux du plugin4/5
Rapport de testTous les tests partagent un dossierContrôler la source du rapport et l’état de swift test4/5
Archive de publicationLe répertoire du build suffitVérifier séparément la commande d’archivage et son fichier final3/5
Cache CILe chemin seul identifie le résultatAjouter outil, architecture, configuration et verrouillage des dépendances5/5
Le score n’est pas une mesure de performance. Il indique la solidité de la méthode pour une automatisation : 5/5 signifie que vous utilisez une information produite par l’outil lui-même, tandis qu’un score inférieur signale une hypothèse à confirmer dans vos journaux.

Scripts et collecte d’artefacts

Le premier chantier concerne les fichiers Shell, Makefile, Fastlane ou scripts de publication. Recherchez les constructions qui ressemblent à :

BUILD_DIR=".build/..."
BIN="$BUILD_DIR/debug/..."
cp "$BIN" "$CI_ARTIFACTS/"

Le problème n’est pas nécessairement la commande cp. Il peut se trouver plusieurs lignes plus tôt, lorsque le script fabrique un chemin à partir de debug, d’une architecture ou d’un nom de produit sans demander à SwiftPM où la tâche a réellement écrit le résultat.

Une version plus robuste conserve les paramètres dans une même liste :

PACKAGE_DIR="${CI_WORKSPACE}/<package>"
CONFIGURATION="debug"
ARCHITECTURE="<architecture>"
DESTINATION="<destination>"

cd "$PACKAGE_DIR" || exit 1

BUILD_ARGS=(
  --configuration "$CONFIGURATION"
  --arch "$ARCHITECTURE"
  --destination "$DESTINATION"
)

swift build "${BUILD_ARGS[@]}"

BIN_PATH="$(
  swift build "${BUILD_ARGS[@]}" --show-bin-path
)" || exit 1

test -d "$BIN_PATH" || {
  printf '%s\n' "Répertoire de sortie absent : $BIN_PATH" >&2
  exit 1
}

find "$BIN_PATH" -type f -maxdepth 2 -print

Adaptez les options disponibles à votre version et à votre type de produit, mais gardez la règle essentielle : la commande de requête doit reprendre la même configuration, la même architecture et la même destination que la commande de compilation. Une requête effectuée en mode debug ne valide pas la collecte d’un build release.

Ne masquez pas les erreurs de résolution :

printf 'workspace=%s\n' "$PWD"
printf 'swift=%s\n' "$(swift --version)"
printf 'bin_path=%s\n' "$BIN_PATH"

Dans Fastlane, transmettez la valeur obtenue à l’étape qui archive ou téléverse le produit, au lieu de dupliquer la logique de chemin dans plusieurs actions. Dans un Makefile, utilisez une cible de découverte séparée, mais appelez-la avec les variables du même contexte. Dans tous les cas, refusez silencieusement un répertoire vide : un téléversement « réussi » d’un mauvais dossier est plus difficile à détecter qu’un échec explicite.

Paquets et plugins

Les auteurs de Swift Package ont un autre risque : leur propre code peut contenir la même hypothèse fragile que le script CI. Examinez les plugins de build, les plugins de commande, les cibles binaires, le traitement des ressources et les scripts qui fabriquent un chemin relatif à partir de .build.

Un plugin devrait déclarer clairement ce qu’il consomme et ce qu’il produit. Il faut pouvoir répondre à quatre questions dans ses journaux :

  • quelles entrées ont été fournies ;
  • quelle commande a été exécutée ;
  • où la sortie a été écrite ;
  • quel fichier précis est ensuite consommé par la phase suivante.
Évitez d’exposer comme contrat public un chemin interne simplement parce qu’il est visible dans une exécution locale. Les [sources officielles de Swift Package Manager](https://github.com/swiftlang/swift-package-manager) et le dépôt [Swift Build](https://github.com/swiftlang/swift-build) sont les références à consulter pour distinguer une commande documentée d’un détail d’implémentation.

Les ressources demandent une vérification distincte. Une image, un fichier audio, une bibliothèque native ou une ressource de design peut être copiée par une étape différente de celle qui produit le binaire. Pour un projet audio ou vidéo, ne validez donc pas uniquement l’existence de l’exécutable : contrôlez aussi les noms, les permissions et la présence des ressources attendues dans le paquet final.

Si vous rencontrez une divergence qui ne s’explique pas par votre script, créez un dossier de problème séparé pour le comportement Swift Build. Le suivi officiel d’une différence de répertoire de sortie doit servir de référence pour documenter un cas reproductible, pas de justification pour ignorer une erreur locale de chemin.

Tests et rapports

La collecte de tests ne doit pas être réduite à test -f rapport. Un pipeline fiable vérifie au minimum trois résultats indépendants :

  1. l’état de sortie de swift test ;
  2. la source réelle du rapport ou du journal ;
  3. la capacité à identifier la suite et le cas ayant échoué.
Un ancien script peut supposer que tous les tests écrivent dans un répertoire unique, ou que chaque exécution utilise le même lanceur. Cette supposition devient dangereuse lorsque la construction et l’exécution ne partagent plus la même organisation de fichiers.

Conservez temporairement deux tâches de comparaison :

swift test \
  --configuration "<configuration>" \
  --parallel

printf 'test_exit=%s\n' "$?"

La commande exacte dépend de votre projet et de votre version d’outillage ; l’important est d’enregistrer son état avant qu’un script de nettoyage ne l’écrase. Ajoutez ensuite une étape qui liste les rapports réellement présents, avec leur chemin absolu, puis archivez uniquement les fichiers attendus.

Pour une comparaison avec le système native, utilisez le même commit, la même configuration, la même architecture et un espace de travail neuf. Si Swift Build réussit et que native échoue, ou inversement, ne concluez pas à un problème du Mac distant avant d’avoir comparé les journaux et les paramètres. Une différence de rapport peut venir du lanceur de test, pas du compilateur.

Cache et nœuds Mac distants

Un cache CI ne doit jamais être identifié par le seul nom du dépôt. Au minimum, son identité doit intégrer :

  • la version de Swift ;
  • le système de build sélectionné ;
  • l’architecture ;
  • la configuration ;
  • la destination ;
  • le fichier de verrouillage des dépendances ;
  • les paramètres qui modifient la production des artefacts.
Sinon, un nœud peut restaurer un résultat produit par une ancienne combinaison d’outils et présenter un chemin qui n’a plus de rapport avec la tâche courante. Le problème est particulièrement trompeur lorsque la compilation semble instantanée et que seule la publication échoue.

Sur chaque Mac distant, contrôlez également :

printf 'user=%s\n' "$USER"
printf 'workspace=%s\n' "$PWD"
xcode-select -p
swift --version
uname -m

Ne prenez pas uname -m comme preuve suffisante de l’architecture réellement ciblée : comparez-le avec l’architecture passée au build et avec celle attendue par le produit. Vérifiez aussi que le compte d’exécution possède les droits de lecture sur le workspace et d’écriture dans le répertoire d’artefacts. Une session SSH, une session VNC et un agent CI peuvent utiliser des environnements différents, notamment pour le PATH, le trousseau et les variables chargées par le shell.

La validation doit couvrir trois situations : cache froid, cache chaud et tâche exécutée après redémarrage du nœud. Un Mac distant utilisé comme nœud permanent doit être testable après une reconnexion SSH, une relance de l’agent et une recréation du workspace. Si votre infrastructure actuelle mélange ces responsabilités, vous pouvez examiner une configuration de développement Swift sur Mac distant avant de déplacer le correctif en production.

Matrice de basculement

Le retour temporaire au système native est un outil de diagnostic, non une réparation automatique. Le changement officiellement documenté du système par défaut doit être confronté aux paramètres de votre dépôt, à vos plugins et à vos rapports de test. La note de développement sur le changement du système par défaut donne le contexte nécessaire, tandis que le dépôt officiel Swift Evolution permet de vérifier le statut des versions et propositions.

<
ParcoursBuildTestsCollecteSignature ou publicationDécision
Swift Build, cache froidRéussi ou échec documentéÉtat conservéChemin interrogéContrôle completBase de référence
Swift Build, cache chaudMême commit validéRapport retrouvéAucun ancien chemin utiliséMême résultatAutoriser l’essai prolongé
Swift Build après redémarrageNœud correctement initialiséAgent reconnectéRépertoire recrééPublication contrôléeValider la reprise
Native, mêmes paramètresComparaisonComparaisonChemin séparéComparaisonDiagnostiquer seulement
Production actuelleRésultat historiqueRésultat historiqueAncienne logiqueLivraison existanteNe pas considérer comme preuve
La sélection peut suivre cette règle :
  • si Swift Build réussit dans les trois conditions et que la collecte interroge son chemin réel, poursuivez avec Swift Build ;
  • si seule la collecte échoue, corrigez le script et le cache avant de changer de système ;
  • si Swift Build et native divergent avec un minimum reproductible, isolez le problème et consultez les problèmes officiels ;
  • si le nœud distant est instable après redémarrage, suspendez le basculement et corrigez d’abord l’environnement ;
  • si la production ne peut pas absorber le risque, maintenez temporairement native, mais fixez une condition explicite de retour à Swift Build.
La documentation Apple consacrée aux ressources Swift doit également être revérifiée lorsque votre pipeline dépend d’un environnement Xcode particulier : [guides Swift sur Apple Developer](https://developer.apple.com/wwdc26/guides/swift/). Au 27 août 2026, Swift 6.4 n’est pas à traiter comme une version stable indépendante tant que le statut officiel ne l’a pas confirmé. De même, ne présentez pas Xcode 27 comme une version finale sans vérifier ses notes de version les plus récentes.

Validation avant livraison

Utilisez cette liste sur un commit identique, d’abord dans un workspace neuf, puis sur un nœud déjà utilisé :

  • [ ] La commande complète de build est enregistrée avec sa version de Swift.
  • [ ] swift build --show-bin-path reprend la configuration du build.
  • [ ] L’architecture et la destination sont identiques entre compilation et interrogation.
  • [ ] Aucun script ne concatène un ancien sous-dossier .build en dur.
  • [ ] Le binaire, les ressources et les éventuels plugins sont contrôlés séparément.
  • [ ] L’état de sortie de swift test est conservé avant le nettoyage.
  • [ ] Le rapport de test permet de retrouver une suite et un cas défaillant.
  • [ ] La clé de cache contient l’outil, le système de build et le verrouillage des dépendances.
  • [ ] Un cache froid et un cache chaud donnent une collecte équivalente.
  • [ ] Le pipeline fonctionne après redémarrage du Mac distant et reconnexion de l’agent.
  • [ ] La tâche native parallèle utilise le même commit et les mêmes paramètres.
  • [ ] Le choix de production, de retour arrière ou d’essai isolé est écrit dans le runbook.
Cette validation répond aussi à la question la plus fréquente des équipes dont le build local fonctionne : le poste local ne représente pas forcément le nœud CI. Le chemin de travail, le compte, le cache, l’architecture ciblée et l’état de l’outil sélectionné peuvent différer, même lorsque les deux machines affichent une version Swift similaire.

Votre solution actuelle, fondée sur un Mac physique dédié ou sur un nœud partagé, peut rester pertinente pour une charge stable, un accès continu à des périphériques physiques ou une longue exploitation amortie. Elle devient toutefois moins confortable lorsque chaque mise à niveau exige de conserver une machine allumée, de nettoyer manuellement les caches, de reproduire un environnement qui dérive et de réserver une capacité pour un incident ponctuel. Pour une campagne de validation isolée, louer un Mac avec MACGPU vous permet de disposer d’un environnement distant réinitialisable, d’exécuter les scénarios à froid et après redémarrage, puis de décider avec des journaux plutôt qu’avec une hypothèse. Consultez les options de Mac distant pour vos essais CI si votre nœud de production ne doit pas être le premier terrain de migration.

Questions fréquentes

SwiftPM 6.4 et les binaires introuvables

Un changement d’organisation interne peut rendre obsolète un chemin codé en dur sous .build, sans empêcher la compilation de produire un résultat. La correction consiste à demander à SwiftPM le répertoire associé aux paramètres courants, puis à faire consommer cette valeur par le script d’archivage. Si l’emplacement interrogé est lui-même incohérent, conservez un minimum reproductible avant de modifier le pipeline.

Récupération du chemin dans le CI

Appelez swift build --show-bin-path après avoir défini le répertoire du paquet et les variables de configuration, d’architecture et de destination. La commande de découverte doit recevoir les mêmes options que la compilation ; sinon, elle peut renvoyer un emplacement valide mais sans rapport avec l’artefact recherché. Vérifiez ensuite le répertoire, listez son contenu et échouez explicitement s’il est vide.

Différence entre Swift Build et native

Les deux systèmes peuvent produire un résultat fonctionnel comparable tout en exposant des chemins, des rapports ou des fichiers intermédiaires différents. La comparaison doit donc porter sur les sorties réellement imprimées, et non sur une reconstitution de .build. Un scénario native qui fonctionne ne démontre pas que votre script Swift Build est correct ; il peut seulement révéler une différence à documenter.

Échec d’envoi depuis un Mac distant

Commencez par déterminer si l’échec se produit avant ou après la production du fichier. Sur le Mac distant, capturez le workspace, le compte, xcode-select, l’architecture, la version Swift, les permissions et le chemin retourné. Nettoyez ensuite le cache et répétez la tâche. Si le problème persiste uniquement avec Swift Build, gardez native comme voie temporaire de diagnostic et ne généralisez pas ce résultat à tous les nœuds.

La correction durable est donc précise : interroger le chemin produit par l’outil, rendre le cache dépendant de l’environnement réel, puis prouver la collecte sur un workspace neuf, un cache existant et un nœud redémarré. Si vous devez isoler ce changement sans exposer votre chaîne de livraison, un Mac distant réinitialisable est généralement plus approprié qu’une modification directe du seul nœud de production.