Installation CocoaPods reproductible sur Mac cloud

DevOps et CI/CD ·~6 min de lecture

Installation CocoaPods reproductible sur Mac cloud

Un même commit peut être compilé sans problème sur une machine de développement, puis résoudre soudainement des dépendances différentes lors d’une nouvelle tâche sur un Mac cloud, ou modifier discrètement Podfile.lock après un pod install. Ce type de problème vient généralement moins de Xcode que du fait que la version de CocoaPods, les caches et le mode d’installation ne sont pas gérés comme des entrées de la compilation. L’objectif n’est pas que « l’installation réussisse à chaque fois », mais que le même ensemble d’entrées produise toujours le même graphe de dépendances et que toute variation interrompe immédiatement le processus.

Définir d’abord les entrées d’une installation reproductible

Le résultat produit par CocoaPods dépend d’au moins cinq entrées : Podfile, Podfile.lock, la version de CocoaPods, l’environnement Ruby et Bundler, ainsi que le code source ciblé par les Pods de développement locaux. Même si le fichier de verrouillage est versionné, laisser l’exécuteur utiliser une version quelconque de CocoaPods peut encore entraîner des différences dans le format du projet généré ou dans le comportement de l’installation.

Le dépôt doit inclure .ruby-version, Gemfile, Gemfile.lock, Podfile et Podfile.lock. Indiquez explicitement la version de CocoaPods dans Gemfile au lieu de dépendre de la dernière version installée globalement sur la machine :

source ENV.fetch("BUNDLE_GEM_SOURCE")

ruby "3.3.4"
gem "cocoapods", "1.15.2"

Dans la CI, utilisez systématiquement bundle exec pod comme point d’entrée. La version affichée dans les journaux correspondra ainsi à l’environnement verrouillé, sans risquer qu’un Shell interactif trouve par hasard la commande globale en priorité.

Podfile.lock verrouille le résultat de la résolution des dépendances, mais pas le programme CocoaPods qui effectue cette résolution et génère le projet.

Utiliser un mode strict pour l’installation

Les compilations courantes ne doivent pas exécuter pod update. Cette commande résout de nouveau les dépendances dans les limites des contraintes de version. Elle convient à une mise à niveau lancée manuellement, pas à la validation d’un commit existant. La CI peut utiliser le point d’entrée suivant :

#!/bin/bash
set -euo pipefail

export LANG=en_US.UTF-8
export COCOAPODS_DISABLE_STATS=true
export CP_HOME_DIR="${RUNNER_TEMP}/cocoapods-home"

bundle config set path "${RUNNER_TEMP}/bundle"
bundle check || bundle install
bundle exec pod install --deployment --clean-install
git diff --exit-code -- Podfile.lock

L’option --deployment provoque un échec si le fichier de verrouillage doit être modifié, tandis que --clean-install réduit le risque qu’un ancien état d’installation masque un problème. Le dernier git diff constitue une seconde barrière : même si un script modifie le fichier de verrouillage pendant l’installation, la tâche ne peut pas poursuivre jusqu’à la compilation.

Si le dépôt contient plusieurs espaces de travail, chacun doit disposer de son propre répertoire d’installation et de son propre fichier journal. Des tâches concurrentes ne doivent jamais partager le même répertoire Pods.

Isoler les caches sans les désactiver aveuglément

Le cache n’est pas un problème en soi ; ce sont ses limites mal définies qui le deviennent. Sur un Mac cloud, les contaminations proviennent souvent d’un cache de téléchargement CocoaPods partagé entre plusieurs dépôts, ou de deux tâches qui lisent et écrivent simultanément dans le même répertoire Bundler. Il est recommandé de construire les clés de cache à partir du « condensat du fichier de verrouillage + version de l’outil » et de n’autoriser l’écriture dans le cache qu’après une installation réussie.

Objet mis en cache Clé de cache recommandée Condition d’invalidation
Répertoire d’installation Bundler Condensat de Gemfile.lock + version de Ruby Modification de l’une des entrées
Cache de téléchargement CocoaPods Condensat de Podfile.lock + version de CocoaPods Modification du fichier de verrouillage ou de l’outil
Répertoire Pods Condensat de Podfile.lock + condensat de la configuration du projet Modification de Podfile, des scripts ou de la configuration

Ne mettez pas Pods/Manifest.lock en cache pour ensuite l’utiliser afin d’écraser le résultat d’une nouvelle installation. Ce fichier décrit l’état réellement installé dans le bac à sable actuel et doit être généré par l’installation en cours. Si une anomalie apparaît après la restauration d’un cache, commencez par effectuer une installation sans cache dans un répertoire isolé. Le problème ne peut être attribué au cache que si cette installation aboutit à un résultat normal.

Vérifier le graphe de dépendances avant la compilation

Une installation réussie ne garantit pas que le graphe de dépendances est correct. Vérifiez au minimum que Podfile.lock et Pods/Manifest.lock sont identiques et qu’aucune modification non validée n’existe :

test -f Podfile.lock
test -f Pods/Manifest.lock

cmp -s Podfile.lock Pods/Manifest.lock || {
  echo "CocoaPods sandbox differs from Podfile.lock" >&2
  exit 1
}

git status --porcelain -- Podfile Podfile.lock Gemfile Gemfile.lock

La tâche doit échouer si la dernière commande produit une sortie, au lieu de se contenter d’afficher un avertissement. Pour les Pods locaux utilisant :path, enregistrez également le numéro de commit ou le condensat du contenu du répertoire cible. Sinon, le contenu du répertoire peut changer sans que le fichier de verrouillage soit modifié, ce qui rend toute reproductibilité illusoire.

Créer un canal distinct pour les mises à niveau

Une tâche de mise à niveau des dépendances peut exécuter pod update SomePod pour une cible précise, mais elle doit présenter trois catégories de différences : les changements de version dans le fichier de verrouillage, les changements de SPEC CHECKSUMS, ainsi que les dépendances transitives ajoutées ou supprimées. Un commit de mise à niveau doit être limité aux dépendances, sans modification simultanée du code métier, afin qu’un retour en arrière puisse simplement restaurer le fichier de verrouillage précédent.

Conserver l’état de l’échec et diagnostiquer dans l’ordre

Après un échec, ne supprimez pas immédiatement tous les caches. Conservez d’abord la version de CocoaPods, la version de Ruby, le condensat du fichier de verrouillage, le journal d’installation et la sortie de git status, puis déterminez à quel niveau se situe le problème.

  1. Si bundle check échoue, vérifiez d’abord Ruby et la plateforme indiquée dans Gemfile.lock.
  2. Si l’installation stricte exige de modifier le fichier de verrouillage, vérifiez si quelqu’un a changé Podfile sans valider le fichier de verrouillage correspondant.
  3. Si seules les tâches restaurant un cache échouent, effectuez une installation sans cache dans un répertoire indépendant et comparez les résultats.
  4. Si Manifest.lock ne correspond pas, vérifiez si l’installation a été interrompue ou si plusieurs tâches partagent le même répertoire Pods.
  5. Si le contenu d’un Pod local dérive, ajoutez une vérification du numéro de commit ou du condensat de son répertoire source.

Un processus CocoaPods stable doit finalement être très prévisible : tant que les entrées ne changent pas, le résultat de l’installation reste identique ; lorsqu’elles changent, les différences passent en revue de code ; lorsqu’un cache est corrompu, seules les performances sont affectées, pas le graphe de dépendances. Placées avant la compilation Xcode, ces vérifications permettent généralement de remplacer un message d’erreur ambigu après plusieurs dizaines de minutes par un échec explicite en quelques dizaines de secondes.

Questions fréquentes

Faut-il lancer pod install ou pod update dans la CI ?

La CI courante doit exécuter pod install avec --deployment. Réservez pod update à une tâche de mise à jour dédiée et révisez les changements de Podfile.lock.

Pourquoi le résultat change-t-il malgré Podfile.lock ?

Les causes fréquentes sont des versions différentes de Ruby ou CocoaPods, des pods locaux modifiables, un cache corrompu ou un script qui réécrit Podfile.

Faut-il versionner le dossier Pods dans Git ?

Généralement non. Versionnez Podfile, Podfile.lock, Gemfile et Gemfile.lock, puis restaurez les dépendances dans la CI à partir de ces entrées figées.

Nœud physique dédié

Choisissez un Mac dans le cloud LemonVM selon la durée de votre mission

Lemon M4 et Lemon M4 Pro sont disponibles à Singapour, Tokyo, Séoul, Hong Kong et dans l’ouest des États-Unis, avec une facturation à la journée, à la semaine, au mois ou au trimestre.

Louer un Mac dans le cloud