Valider les variantes App Thinning sur un Mac cloud

Valider les variantes App Thinning sur un Mac cloud

Lorsqu’une même archive Xcode passe par le processus de distribution, le contenu finalement installé n’est pas strictement identique sur tous les appareils. App Thinning sélectionne les tranches d’exécutable et les variantes de ressources en fonction des capacités de chaque appareil. Les builds habituels se limitent souvent à vérifier que l’archivage a réussi, ce qui peut laisser passer une erreur d’étiquetage des ressources, l’inclusion d’une architecture inutile, une dérive des frameworks intégrés ou même l’absence totale du rapport de taille. Une approche plus fiable consiste à séparer, sur un Mac cloud, l’archivage et la validation des variantes d’appareil en deux étapes indépendantes.

Cet article s’appuie sur un projet existant et une configuration d’archivage prête pour la distribution. Il ne modifie pas le système de signature et n’impose pas de recompiler à chaque exécution. L’objectif est d’exporter les résultats App Thinning à partir d’une seule archive .xcarchive, de générer un inventaire de leur contenu et de faire en sorte que la CI renvoie un code de sortie non nul en cas d’anomalie clairement définie.

Figer les entrées de validation

Avant de lancer la validation, consignez le chemin de Xcode, la version de l’application, le numéro de build et l’emplacement de l’archive. La tâche ne doit pas dépendre de la chaîne d’outils par défaut d’un shell interactif. Plusieurs versions de Xcode peuvent coexister sur un Mac cloud : la CI doit sélectionner explicitement celle qui a été approuvée pour le pipeline en cours.

set -euo pipefail

export DEVELOPER_DIR="/Applications/Xcode.app/Contents/Developer"
ARCHIVE_PATH="$PWD/output/App.xcarchive"
EXPORT_DIR="$PWD/output/thinning"
REPORT_DIR="$PWD/output/evidence"

xcodebuild -version
test -d "$ARCHIVE_PATH"
rm -rf "$EXPORT_DIR" "$REPORT_DIR"
mkdir -p "$EXPORT_DIR" "$REPORT_DIR"

Lisez ensuite les informations de l’application contenues dans l’archive afin de vérifier que la CI n’a pas récupéré par erreur un artefact laissé par une tâche précédente.

INFO="$ARCHIVE_PATH/Info.plist"

/usr/libexec/PlistBuddy -c "Print :ApplicationProperties:CFBundleIdentifier" "$INFO"
/usr/libexec/PlistBuddy -c "Print :ApplicationProperties:CFBundleShortVersionString" "$INFO"
/usr/libexec/PlistBuddy -c "Print :ApplicationProperties:CFBundleVersion" "$INFO"

Il est recommandé d’associer le répertoire de travail à l’identifiant de la tâche et de créer un répertoire vide au démarrage. N’utilisez pas un répertoire output global et fixe qui obligerait plusieurs tâches concurrentes à partager leurs archives.

L’entrée de la validation App Thinning doit être une archive .xcarchive unique ayant déjà passé les contrôles d’archivage habituels. Si l’entrée elle-même n’est pas fiable, une comparaison détaillée des variantes d’appareil n’a aucun intérêt.

Exporter toutes les variantes d’appareil compatibles

Préparez un fichier ExportOptions.plist distinct. La valeur de method doit rester cohérente avec la version de Xcode actuellement utilisée par l’équipe et avec le processus de distribution existant. Dans l’exemple ci-dessous, le point essentiel consiste à configurer thinning pour exporter toutes les variantes compatibles.

<?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>app-store-connect</string>
  <key>thinning</key>
  <string>&lt;thin-for-all-variants&gt;</string>
  <key>stripSwiftSymbols</key>
  <true/>
</dict>
</plist>

Pendant l’exportation, placez les résultats et les journaux dans le répertoire de la tâche en cours :

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

find "$EXPORT_DIR" -maxdepth 3 -type f -print \
  | LC_ALL=C sort \
  > "$REPORT_DIR/export-files.txt"

Si le pipeline existant emploie une autre méthode d’exportation, ne la changez pas uniquement pour reproduire cet exemple. Conservez la valeur actuelle de method, validez la configuration de thinning dans une branche isolée et archivez également le fichier plist utilisé.

Contrôler les architectures et le contenu intégré

Une exportation réussie ne garantit pas que le résultat est correct. Commencez par localiser chaque paquet d’installation et décompressez-les un par un, puis contrôlez l’architecture de l’exécutable principal. Le script ne doit pas supposer que le répertoire de l’application porte toujours le même nom : il peut découvrir le paquet .app dans Payload.

mkdir -p "$REPORT_DIR/unpacked"

while IFS= read -r ipa; do
  name="$(basename "$ipa" .ipa)"
  dest="$REPORT_DIR/unpacked/$name"
  mkdir -p "$dest"
  ditto -x -k "$ipa" "$dest"

  app="$(find "$dest/Payload" -maxdepth 1 -name '*.app' -type d | head -n 1)"
  test -n "$app"

  executable="$(/usr/libexec/PlistBuddy \
    -c 'Print :CFBundleExecutable' "$app/Info.plist")"

  printf '%s: ' "$name"
  lipo -archs "$app/$executable"

  find "$app/Frameworks" -maxdepth 1 -type d -name '*.framework' 2>/dev/null \
    | sed "s#^$app/##" \
    | LC_ALL=C sort \
    > "$REPORT_DIR/$name-frameworks.txt"
done < <(find "$EXPORT_DIR" -type f -name '*.ipa' | LC_ALL=C sort)

Pour une distribution destinée aux appareils iOS actuels, la présence d’une architecture de simulateur dans l’application principale doit provoquer un échec immédiat. L’inventaire des frameworks peut quant à lui être comparé à une référence approuvée et conservée dans le dépôt. L’ajout d’un framework n’est pas nécessairement une erreur, mais il doit faire l’objet d’une revue de code afin d’éviter qu’un composant de débogage ou une dépendance temporaire ne se retrouve dans le paquet livré.

Ne pas comparer le hachage du paquet d’installation complet

Des variantes destinées à différents appareils peuvent légitimement contenir des ressources et des tranches d’exécutable différentes. Les données de signature produisent elles aussi des différences binaires. Exiger que tous les fichiers IPA aient le même SHA-256 reviendrait donc à considérer un amincissement normal comme un échec.

Il est plus pertinent de comparer les éléments suivants :

  • les architectures autorisées pour l’exécutable principal ;
  • l’inventaire des chemins relatifs dans Frameworks et PlugIns ;
  • les fichiers de localisation, polices et données qui doivent être présents ;
  • les configurations de débogage, ressources de test et fichiers source qui ne doivent pas figurer dans le produit final ;
  • la taille décompressée de chaque variante et son évolution par rapport à la référence historique.

Transformer les ressources et la taille en preuves

Vérifiez d’abord que le processus d’exportation a bien produit un rapport de taille, puis générez l’inventaire des fichiers de chaque variante. La taille des fichiers étant affectée par l’élagage des ressources, chaque variante doit disposer de sa propre référence.

SIZE_REPORT="$(find "$EXPORT_DIR" -type f \
  -name 'App Thinning Size Report.txt' | head -n 1)"

test -n "$SIZE_REPORT"
cp "$SIZE_REPORT" "$REPORT_DIR/"

for app in "$REPORT_DIR"/unpacked/*/Payload/*.app; do
  variant="$(basename "$(dirname "$(dirname "$app")")")"
  (
    cd "$app"
    find . -type f -print0 \
      | xargs -0 stat -f '%z %N' \
      | LC_ALL=C sort
  ) > "$REPORT_DIR/$variant-files-and-sizes.txt"
done

Le contrôle de taille ne doit pas reposer sur une seule valeur absolue figée. Il est recommandé de conserver deux critères : une limite stricte approuvée par l’équipe et un seuil de croissance par rapport à la dernière référence stable. La limite stricte empêche les dérives manifestes, tandis que la comparaison relative détecte les augmentations anormales introduites par un seul commit.

Pour les répertoires de ressources, vérifiez également la présence des fichiers essentiels au fonctionnement métier. Par exemple, l’intégrité des modèles hors ligne, des polices du premier écran ou des ressources d’une langue donnée ne peut pas être démontrée par le simple fait que l’application démarre. Conservez la liste de ces chemins dans un fichier texte du dépôt et exécutez test -e pour chacun d’eux. Cette approche est plus facile à examiner qu’une multitude de conditions dispersées dans le script.

Définir des conditions d’échec explicites dans la CI

Le script final doit au minimum bloquer les cas suivants : aucun IPA exporté, rapport de taille manquant, architecture non autorisée dans l’application principale, ressource essentielle absente, modification non approuvée de l’ensemble des frameworks ou taille d’une variante supérieure à la limite fixée par l’équipe.

Conservez également les preuves suivantes :

  1. la version de Xcode et la valeur de DEVELOPER_DIR ;
  2. l’identifiant de paquet, la version et le numéro de build de l’archive ;
  3. le fichier ExportOptions.plist réellement utilisé ;
  4. le journal d’exportation complet ;
  5. les inventaires des architectures, frameworks et fichiers de chaque variante ;
  6. le rapport de taille App Thinning.

En cas d’échec, ne supprimez pas immédiatement le répertoire d’exportation. Regroupez et téléversez d’abord les preuves, puis laissez le hook de sortie de la tâche nettoyer l’espace de travail. En cas d’exécution concurrente sur les Mac cloud de PingMini, chaque tâche doit utiliser un répertoire distinct et la configuration actuellement disponible doit être vérifiée dans la console. La logique de validation elle-même ne doit dépendre d’aucun nœud fixe.

App Thinning ne sert pas uniquement à réduire la taille des téléchargements : il modifie aussi le livrable final. Ce n’est qu’en traitant les variantes d’appareil comme de véritables artefacts de build qu’il devient possible d’obtenir, avant la publication, des échecs reproductibles et faciles à diagnostiquer lorsqu’une ressource, une architecture ou une dépendance change.

Questions fréquentes

Faut-il contrôler l’archive Xcode ou les paquets exportés ?

Il faut contrôler les deux. L’archive valide la source commune, tandis que les paquets exportés révèlent les architectures, ressources et composants réellement présents dans chaque variante.

Des empreintes différentes entre variantes signalent-elles une erreur ?

Non. Les tranches exécutables, ressources et données de signature peuvent différer. Il faut comparer les inventaires, les architectures autorisées et les ressources obligatoires.

Quelles anomalies doivent bloquer la CI ?

L’absence de rapport de taille, un export vide, une architecture interdite, une ressource obligatoire absente, une dérive des frameworks ou un dépassement de taille doivent bloquer le job.

Préparez un Mac physique dédié pour votre prochain projet

Choisissez parmi trois configurations fixes de Mac dans le cloud, adaptées au développement, à l’intégration continue ou aux tests de modèles locaux, puis configurez-le selon la durée de votre tâche.

Configurer un Mac dans le cloud