Bonnes pratiques d’ingénierie

Diagnostiquer un échec de signature iOS causé par les attributs étendus

Diagnostiquer un échec de signature iOS causé par les attributs étendus

L’archivage fonctionne en local, mais échoue à l’étape de signature sur un Mac dans le cloud avec le message resource fork, Finder information, or similar detritus not allowed. Ce type d’erreur ne vient généralement pas d’un certificat ou d’un profil de provisionnement expiré, mais d’une image, d’un artefact de script ou d’un répertoire copié qui contient des attributs étendus macOS. Ces métadonnées sont intégrées au bundle de l’App lors de la copie des ressources et ne sont détectées qu’au moment où l’outil de signature effectue ses contrôles stricts.

Identifier d’abord la couche où survient l’échec

Ne recréez pas immédiatement les certificats dès que vous voyez un « échec de signature ». Commencez par repérer la première erreur codesign dans le journal de compilation complet, puis notez si l’objet concerné est l’App principale, un Framework, une extension ou un bundle de ressources. Si le journal mentionne explicitement FinderInfo, ResourceFork ou detritus, concentrez l’analyse sur les métadonnées des fichiers.

Les attributs étendus se rencontrent souvent dans les emplacements suivants :

  • images, polices ou archives copiées dans le dépôt depuis un gestionnaire de fichiers graphique ;
  • ressources extraites d’un répertoire partagé, puis validées directement dans le dépôt ;
  • répertoires prégénérés déplacés par un script de build avec cp -R ;
  • artefacts locaux non suivis par le contrôle de version, mais conservés depuis longtemps dans le répertoire de travail ;
  • dépendances restaurées depuis un cache avec leurs métadonnées de fichiers.

La suppression de DerivedData n’efface que les sorties de compilation. Si la contamination se trouve dans les sources ou dans une entrée externe, elle réapparaîtra systématiquement au build suivant.

Conservez d’abord la commande ayant échoué, le chemin de la cible et les journaux, puis procédez au nettoyage. Sinon, même si le build se remet à fonctionner par hasard, vous ne pourrez pas identifier la véritable source de la contamination.

Établir une base de référence en lecture seule

Examiner les entrées du dépôt

xattr -lr répertorie récursivement les chemins et leurs attributs. Commencez par enregistrer les résultats dans un journal sans supprimer les attributs pendant l’analyse :

set -euo pipefail

ROOT="${SRCROOT:-$PWD}"
REPORT="${TMPDIR:-/tmp}/ios-xattr-source.log"

xattr -lr "$ROOT" > "$REPORT" 2>&1 || true

grep -E \
  'com\.apple\.(FinderInfo|ResourceFork)' \
  "$REPORT" || true

Si le dépôt est volumineux, vous pouvez exclure .git, DerivedData et les caches de dépendances, puis examiner séparément les répertoires réellement inclus dans le bundle. Le nombre de correspondances importe moins que les réponses à trois questions : à quel fichier l’attribut est-il attaché, quel processus a généré ce fichier et par quelle Build Phase entre-t-il dans le bundle ?

Emplacement détecté Vérification prioritaire Principe de traitement
Assets ou répertoire de ressources Méthode d’importation, outil de décompression Corriger le fichier source
Répertoire de sortie d’un script Paramètres de cp et ditto Recréer le répertoire temporaire
DerivedData Restauration du cache, anciens artefacts Vider le cache et reproduire
Intérieur d’un Framework Étapes de téléchargement et d’extraction Télécharger de nouveau et contrôler

Examiner le bundle archivé

Si l’archive a déjà été générée, analysez directement le fichier .app pour confirmer que la contamination a atteint le livrable :

ARCHIVE_PATH="$PWD/build/App.xcarchive"
APP_PATH="$ARCHIVE_PATH/Products/Applications/App.app"
REPORT="$PWD/build/xattr-app.log"

test -d "$APP_PATH"
xattr -lr "$APP_PATH" > "$REPORT" 2>&1 || true

if grep -Eq 'com\.apple\.(FinderInfo|ResourceFork)' "$REPORT"; then
  echo "forbidden extended attributes found"
  exit 1
fi

Cet exemple utilise un chemin fixe. Dans un pipeline réel, il faut identifier l’unique fichier .app à partir du répertoire d’archive et arrêter immédiatement l’exécution si aucun candidat ou plusieurs candidats sont trouvés, afin de ne pas contrôler le mauvais bundle.

Nettoyer sans perdre la traçabilité

Il est déconseillé d’exécuter immédiatement xattr -cr sur l’ensemble de l’espace de travail. Cette commande supprime tous les attributs étendus, élargit inutilement la portée des modifications et peut masquer un problème dans les étapes de téléchargement, d’extraction ou de restauration du cache. Une méthode plus sûre consiste à supprimer uniquement les deux types d’attributs dont l’impact sur la signature a été confirmé :

TARGET="$PWD/StagingPayload"

find "$TARGET" -print0 |
while IFS= read -r -d '' item; do
  xattr -d com.apple.FinderInfo "$item" 2>/dev/null || true
  xattr -d com.apple.ResourceFork "$item" 2>/dev/null || true
done

Dans l’idéal, TARGET doit désigner une copie temporaire à usage unique plutôt que l’espace de travail d’origine du développeur. Une fois le nettoyage terminé, relancez l’analyse. Si des correspondances subsistent, interrompez le build et n’ajoutez pas || true à la condition de validation finale.

Si les attributs proviennent d’une archive compressée, corrigez l’étape de contrôle qui suit son extraction. S’ils viennent d’un script de build, faites en sorte que le script crée à chaque exécution un répertoire temporaire vide, puis copie une liste explicite de fichiers. Ajouter simplement une commande de nettoyage récursif à la fin laisse la contamination en place et lui permet de réapparaître dans d’autres tâches.

Intégrer les contrôles au processus d’archivage

Contrôler les entrées avant le build

Le contrôle préalable au build doit analyser uniquement les répertoires de ressources qui seront intégrés à l’App, sans parcourir tout le répertoire personnel. En cas de correspondance, affichez le chemin relatif et le nom de l’attribut, puis quittez avec un code différent de zéro. Le pipeline échouera ainsi avant le lancement d’un archivage coûteux, tout en facilitant l’identification de la modification récente en cause.

Le script de contrôle doit être placé sous gestion de versions et exécuté dans un environnement déterministe :

  1. déterminer la racine du dépôt à partir de l’emplacement du script lui-même ;
  2. utiliser des séparateurs nuls pour les chemins contenant des espaces ;
  3. écrire le rapport dans un répertoire d’artefacts propre à la tâche en cours ;
  4. ne modifier aucun fichier pendant l’analyse ;
  5. signaler immédiatement toute absence d’un répertoire cible.

Valider la signature après l’archivage

Une fois l’analyse des attributs étendus réussie, utilisez l’outil de signature du système pour vérifier l’archive :

APP_PATH="$PWD/build/App.xcarchive/Products/Applications/App.app"

codesign --verify \
  --strict \
  --verbose=2 \
  "$APP_PATH"

Si l’App contient des Frameworks ou des extensions indépendants, parcourez également ces objets de code imbriqués afin de les vérifier séparément. Ne supposez pas que les composants internes sont valides après avoir contrôlé uniquement le bundle principal. Conservez ensemble les journaux de validation, le chemin de l’archive et l’identifiant du commit source afin de distinguer les cas où les sources sont identiques, mais les entrées restaurées depuis le cache diffèrent.

Terminer le diagnostic avec une liste de validation

Une correction fiable doit remplir simultanément les conditions suivantes :

  • les résultats d’analyse des sources et des entrées externes ont été archivés ;
  • le processus ayant généré ou copié le fichier contaminé a été identifié ;
  • le nettoyage a ciblé uniquement les attributs concernés ou une copie temporaire à usage unique ;
  • une nouvelle archive peut être produite après suppression de DerivedData et des caches associés ;
  • le bundle final de l’App ne contient ni FinderInfo ni ResourceFork ;
  • l’App principale et les objets de code imbriqués passent tous une vérification stricte de la signature ;
  • le même commit réussit de manière reproductible dans un nouveau répertoire de travail.

Les problèmes d’attributs étendus sont difficiles à diagnostiquer non pas parce que les commandes sont complexes, mais parce qu’ils traversent quatre couches : le code source, les métadonnées du système de fichiers, le cache et la signature. En transformant la séquence « enregistrer, localiser, nettoyer, valider » en contrôles obligatoires, ces incidents ne nécessitent plus de se connecter manuellement à un nœud pour effectuer une correction ponctuelle, et les indices ne disparaissent plus lors d’un changement de répertoire de travail.

Questions fréquentes

Pourquoi supprimer DerivedData ne corrige-t-il pas toujours l’erreur ?

Si FinderInfo ou ResourceFork est présent dans les sources, un dossier copié par script ou un fichier téléchargé, chaque reconstruction le réintroduit. Il faut analyser les entrées avant de recréer DerivedData.

Faut-il exécuter xattr -cr sur tout le dépôt ?

Non. Commencez par inventorier les fichiers concernés, puis supprimez seulement FinderInfo et ResourceFork. Réservez le nettoyage récursif complet à une copie de travail temporaire.

Comment valider l’archive après le nettoyage ?

Analysez le bundle App pour vérifier l’absence des attributs ciblés, puis lancez codesign --verify --strict. Conservez également les journaux et la somme de contrôle de l’archive.

NowMini M4

Lancez votre prochaine tâche sur un nœud physique dédié

Utilisez un Mac dans le cloud équipé d’une puce M4, de 16 Go de RAM et d’un SSD de 256 Go, avec un nœud à Singapour, Tokyo, Séoul ou Hong Kong selon la durée de votre tâche.

Louer un Mac dans le cloud