Diagnostiquer l’échec d’un export Xcode sur un Mac dans le cloud

Diagnostiquer l’échec d’un export Xcode sur un Mac dans le cloud

L’archivage est indiqué comme réussi, mais le pipeline s’interrompt à l’étape -exportArchive. Dans ce type d’incident, la dernière ligne « export failed » conduit facilement à un mauvais diagnostic. La cause réelle peut être une incompatibilité entre la méthode d’export et les éléments de signature, une modification des autorisations dans l’archive ou l’utilisation d’une autre installation de Xcode par la CI. Plutôt que de relancer le job à répétition, il faut conserver le même xcarchive et séparer l’archivage, l’export et le diagnostic en étapes reproductibles indépendamment.

Commencer par figer l’état de l’échec

Après un échec, ne nettoyez pas immédiatement le répertoire de travail et n’écrasez pas l’archive d’origine. Conservez au minimum le xcarchive, le fichier ExportOptions.plist réellement utilisé, la sortie standard complète, ainsi que le chemin et la version de Xcode. Les nœuds physiques dédiés de RunnerVM se prêtent bien à la conservation de cet état, mais le pipeline doit malgré tout copier explicitement les fichiers de diagnostic dans le répertoire des artefacts du job.

set -euo pipefail

RUN_DIR="$PWD/export-diagnostics"
ARCHIVE_PATH="$PWD/build/App.xcarchive"

mkdir -p "$RUN_DIR"
xcode-select -p > "$RUN_DIR/xcode-path.txt"
xcodebuild -version > "$RUN_DIR/xcode-version.txt"
sw_vers > "$RUN_DIR/macos-version.txt"
cp ExportOptions.plist "$RUN_DIR/ExportOptions.plist"
ditto "$ARCHIVE_PATH" "$RUN_DIR/App.xcarchive"

Enregistrez également la somme de contrôle de l’archive. Si quelqu’un la remplace par la suite, vous saurez immédiatement que l’objet de l’analyse a changé.

ditto -c -k --keepParent "$ARCHIVE_PATH" "$RUN_DIR/App.xcarchive.zip"
shasum -a 256 "$RUN_DIR/App.xcarchive.zip" > "$RUN_DIR/checksums.txt"

Un échec de l’export ne signifie pas que l’archive est invalide. Vérifiez d’abord son intégrité, puis déterminez si le problème relève de l’archivage ou de la distribution.

Vérifier que l’archive peut être exportée

Un xcarchive est essentiellement un répertoire. Commencez par vérifier la présence des métadonnées, du bundle de l’application et du fichier exécutable, puis relevez l’identifiant et la version de l’application déclarés dans l’archive. Ne construisez pas le chemin à partir du nom du projet : récupérez-le dans Info.plist.

INFO="$ARCHIVE_PATH/Info.plist"
APP_RELATIVE=$(/usr/libexec/PlistBuddy -c \
  "Print :ApplicationProperties:ApplicationPath" "$INFO")
APP_PATH="$ARCHIVE_PATH/Products/$APP_RELATIVE"

plutil -lint "$INFO"
test -d "$APP_PATH"
test -f "$APP_PATH/Info.plist"

plutil -p "$INFO"
plutil -p "$APP_PATH/Info.plist"
codesign --verify --deep --strict --verbose=2 "$APP_PATH"

Contrôlez en priorité ApplicationPath, CFBundleIdentifier, le numéro de version et le résultat de la validation de signature. Si le bundle de l’application est absent ou si codesign --verify échoue déjà, il faut revenir à la commande archive, aux scripts des phases de build ou au processus de copie des artefacts, plutôt que de continuer à modifier les options d’export.

Comparer les autorisations de l’application et le profil de provisionnement

Lors de l’export, Xcode réévalue les relations de signature. Exportez séparément les autorisations effectives de l’application et celles du profil de provisionnement intégré afin de les comparer de manière structurée.

codesign -d --entitlements "$RUN_DIR/app-entitlements.plist" "$APP_PATH"

PROFILE="$APP_PATH/embedded.mobileprovision"
security cms -D -i "$PROFILE" > "$RUN_DIR/profile.plist"

plutil -extract Entitlements xml1 \
  -o "$RUN_DIR/profile-entitlements.plist" \
  "$RUN_DIR/profile.plist"

plutil -p "$RUN_DIR/app-entitlements.plist"
plutil -p "$RUN_DIR/profile-entitlements.plist"

Ne vous contentez pas de comparer l’ordre textuel des entrées. Vérifiez que les autorisations utilisées par l’application sont permises par le profil de provisionnement et que l’identifiant de l’application, l’identifiant d’équipe ainsi que les autorisations propres à l’environnement concordent. Chaque cible d’extension doit également être contrôlée séparément : la validation de l’application principale ne garantit pas que tous les composants intégrés puissent être exportés.

Valider ExportOptions.plist

Exécutez d’abord plutil -lint ExportOptions.plist pour écarter les erreurs de format, puis consultez l’aide de la version actuelle de Xcode afin de confirmer les clés et les valeurs disponibles. Les valeurs acceptées pour method peuvent évoluer d’une version à l’autre et d’anciennes syntaxes peuvent devenir obsolètes. Il ne faut donc pas reprendre directement la configuration d’un ancien job sans la vérifier.

Une configuration minimale peut se limiter aux champs réellement nécessaires au processus de distribution actuel :

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "https://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>method</key>
  <string>development</string>
  <key>signingStyle</key>
  <string>manual</string>
  <key>stripSwiftSymbols</key>
  <true/>
</dict>
</plist>

La valeur de method présentée ici n’est qu’un exemple d’export pour le développement et ne doit pas être reprise telle quelle pour d’autres modes de distribution. Exécutez d’abord les commandes suivantes et suivez les indications fournies par la chaîne d’outils actuelle :

plutil -lint ExportOptions.plist
xcodebuild -help > export-help.txt
grep -A 120 "Available keys for -exportOptionsPlist" export-help.txt

En cas de signature manuelle, vérifiez également que les correspondances définies dans la configuration couvrent l’application principale et chacune de ses extensions. Une correspondance manquante, un identifiant incorrect ou le mélange de plusieurs modes de signature ne se manifeste souvent qu’au moment de l’export.

Rejouer l’export séparément et conserver le journal complet

Une fois la validité de l’archive confirmée, exécutez l’export séparément avec des chemins absolus fixes. Le script ne doit pas supprimer immédiatement les répertoires temporaires en cas d’échec.

set +e

xcodebuild -exportArchive \
  -archivePath "$ARCHIVE_PATH" \
  -exportPath "$RUN_DIR/exported" \
  -exportOptionsPlist "$RUN_DIR/ExportOptions.plist" \
  2>&1 | tee "$RUN_DIR/export.log"

STATUS=${PIPESTATUS[0]}
set -e

printf '%s
' "$STATUS" > "$RUN_DIR/export-status.txt"
exit "$STATUS"

Dans le journal, recherchez d’abord la première erreur précise plutôt que le récapitulatif final. Vous pouvez effectuer un premier filtrage sur la signature, les profils de provisionnement, les autorisations et la méthode d’export :

grep -Ein \
  "error:|provision|entitlement|certificate|signing|export" \
  "$RUN_DIR/export.log" > "$RUN_DIR/export-errors.txt" || true

Il arrive que xcodebuild indique dans sa sortie le chemin d’un paquet de journaux de distribution. Archivez ce répertoire dans son intégralité. Il contient généralement les décisions prises à chaque étape du processus IDEDistribution et permet de remonter plus directement à la cause réelle que les quelques lignes d’erreur conservées par le pipeline.

Établir une liste de contrôle après correction

Après la correction, ne vous fiez pas uniquement au code de sortie. Vérifiez au minimum que le répertoire d’export contient le fichier attendu, que la validation de signature réussit et que l’identifiant ainsi que la version de l’application correspondent à ceux de l’archive. Conservez également le fichier ExportOptions.plist final avec la version de Xcode utilisée.

Il est recommandé d’intégrer les contrôles suivants à la fin du job :

  1. La somme de contrôle de l’archive compressée n’a pas changé.
  2. La valeur actuelle de xcode-select -p correspond à la chaîne d’outils attendue.
  3. L’application principale et toutes les extensions passent la validation de signature.
  4. Les autorisations de l’application ne dépassent pas celles permises par le profil de provisionnement.
  5. La configuration d’export passe le contrôle plutil, et ses clés et valeurs sont prises en charge par la version actuelle de Xcode.
  6. Le journal d’export, le paquet de journaux de distribution et les artefacts finaux sont conservés ensemble.
  7. La correction ne modifie qu’une seule variable et peut être validée à plusieurs reprises sur l’archive d’origine.

Avec cette méthode, l’export d’une archive n’est plus une opération opaque et difficile à expliquer. L’équipe peut distinguer clairement une dérive de la chaîne d’outils, une erreur de configuration, une anomalie dans les relations de signature ou une archive endommagée, puis valider la plupart des corrections sans relancer la compilation.

Questions fréquentes

Faut-il recréer immédiatement l’archive lorsqu’un export échoue ?

Non. Conservez le xcarchive d’origine et examinez ses métadonnées, son application, son profil intégré et ses autorisations. Une nouvelle archive peut supprimer les indices du premier échec.

Peut-on partager un ExportOptions.plist entre plusieurs versions de Xcode ?

Pas sans validation. Consultez l’aide de xcodebuild fournie par la version Xcode active, vérifiez les valeurs method acceptées et validez la syntaxe du fichier avant l’export.

Runner M4

Faites fonctionner votre prochaine compilation sur un Mac physique dédié

Choisissez un nœud Mac dans le cloud adapté à la durée de votre tâche, puis gérez vos commandes, informations de connexion et tickets d’assistance depuis la console.

Choisir une offre et commander