Diagnostiquer et restaurer sans risque les objets Git en CI Mac cloud

Diagnostiquer et restaurer sans risque les objets Git en CI Mac cloud

Lorsqu’un pipeline signale soudainement bad object, missing blob ou pack checksum mismatch à l’étape de checkout, une nouvelle exécution peut rétablir la situation, mais aussi provoquer l’échec simultané d’autres tâches. Ce type d’incident vient généralement non pas du code applicatif, mais d’une incohérence dans l’espace de travail, le cache d’objets ou les packfiles Git. La bonne méthode ne consiste pas à vider immédiatement le cache : il faut d’abord interrompre les écritures, préserver l’état des lieux, puis déterminer si la corruption touche un seul dépôt ou une couche partagée.

Distinguer une panne réseau d’une corruption d’objets

Les pannes réseau surviennent le plus souvent pendant la récupération des données. Les journaux mentionnent généralement une connexion interrompue, une fermeture prématurée par le serveur distant ou un transfert incomplet. Une corruption d’objets apparaît plutôt lors du checkout, d’une fusion, de la lecture de l’historique ou de la création d’une archive, souvent avec le hash précis d’un objet. Commencez par consigner la commande en échec, le code de sortie, l’identifiant du commit, le chemin de l’espace de travail et la génération du cache. Ne conservez pas uniquement les dernières dizaines de lignes du pipeline.

Vous pouvez commencer par ces contrôles en lecture seule :

git status --porcelain=v2
git rev-parse --show-toplevel
git rev-parse HEAD
git fsck --full --strict --no-dangling

Si git fsck signale missing blob, un objet représentant un fichier et accessible depuis une référence est absent. Un message missing tree empêche généralement le checkout d’une arborescence, tandis que invalid sha1 pointer indique qu’une référence pointe vers un objet illisible. La seule présence d’objets dangling ne permet pas de conclure à une corruption du dépôt : ils peuvent provenir d’un rebase, d’une compression ou de commits remplacés.

Pendant le diagnostic, l’objectif est de délimiter la corruption, et non de faire temporairement repasser la commande au vert. Toute opération susceptible de réécrire la base d’objets doit être reportée jusqu’à la sauvegarde complète de l’état des lieux.

Geler l’espace de travail et conserver les preuves

Commencez par suspendre les runners, les récupérations planifiées et les tâches de mise à jour du cache qui écrivent dans le même répertoire. N’exécutez pas git gc, git prune, de réempaquetage ni de suppression récursive dans le répertoire d’origine. Ces opérations peuvent modifier la disposition des objets et empêcher de reproduire l’anomalie initiale.

Conservez au minimum les éléments suivants : le journal complet de la tâche, .git/HEAD, .git/config, .git/packed-refs, .git/refs, .git/logs, l’état courant et les modifications non validées. L’espace de travail peut contenir des fichiers non suivis. Examinez-en la liste avant de créer une archive afin de ne pas y inclure des identifiants secrets ou des artefacts de build volumineux.

incident="$HOME/git-incidents/$(date +%Y%m%d-%H%M%S)"
mkdir -p "$incident"
git status --porcelain=v2 > "$incident/status.txt"
git show-ref --head > "$incident/refs.txt"
git reflog show --all --date=iso > "$incident/reflog.txt"
git diff --binary > "$incident/worktree.patch"
git diff --cached --binary > "$incident/index.patch"
git fsck --full --strict > "$incident/fsck.txt" 2>&1 || true

Si l’espace de travail peut être recréé à tout moment par la CI, la solution la plus sûre consiste à renommer et isoler tout le répertoire plutôt qu’à le réparer sur place. Placez le répertoire isolé en lecture seule et consignez la tâche ainsi que la source du cache auxquelles il correspond.

Localiser la corruption dans les objets libres, les packfiles et les caches partagés

Les objets Git peuvent être stockés sous forme de fichiers libres ou regroupés dans un fichier .pack. Si l’erreur fournit un hash, commencez par interroger le type et la taille de l’objet :

object="0123456789abcdef0123456789abcdef01234567"
git cat-file -t "$object"
git cat-file -s "$object"

Si la commande échoue, vérifiez si .git/objects/${object:0:2}/${object:2} existe. Si le fichier est présent mais illisible, son contenu est généralement tronqué, ne correspond pas à sa somme de contrôle ou résulte d’une copie incomplète au niveau du stockage. Ne copiez pas sur l’état des lieux un fichier portant le même nom depuis une autre source non vérifiée.

Pour les packfiles, vérifiez chaque index séparément :

for index in .git/objects/pack/*.idx; do
  git verify-pack -v "$index" >/dev/null || printf '%s\n' "$index"
done

Si le dépôt utilise des alternates, examinez également .git/objects/info/alternates. Lorsque plusieurs tâches partagent le même répertoire d’objets accessible en écriture, une seule écriture interrompue peut affecter tous les espaces de travail qui le référencent. Dans ce cas, recloner un dépôt isolé ne supprime pas la cause racine : il faut interrompre la publication de cette génération du cache.

Vérifier si la corruption est reproductible

Copiez le répertoire isolé avant de reproduire les contrôles. Consignez également l’espace restant sur le système de fichiers, la manière dont le processus s’est terminé et le nombre de tâches exécutées pendant la même période. Si l’échec concerne toujours le même objet, examinez en priorité la source des objets et le cache. Si l’objet en échec change, vérifiez la pression exercée sur le disque, les écritures concurrentes et la logique de nettoyage de l’espace de travail.

Restaurer à partir d’un clone propre plutôt que réparer sur place

Pour un espace de travail CI ne contenant aucune modification manuelle, la méthode de restauration la plus sûre consiste généralement à créer un nouveau répertoire, à récupérer intégralement le commit cible, à effectuer les vérifications, puis à remplacer atomiquement l’ancien répertoire. Le nouveau clone ne doit pas réutiliser un cache d’objets qui n’a pas encore été validé.

root="$HOME/ci-workspaces"
next="$root/project.next"
active="$root/project"
failed="$root/project.failed"

rm -rf "$next"
git clone --no-local "$REPOSITORY_PATH" "$next"
git -C "$next" checkout --detach "$EXPECTED_COMMIT"
git -C "$next" fsck --full --strict
test "$(git -C "$next" rev-parse HEAD)" = "$EXPECTED_COMMIT"
mv "$active" "$failed"
mv "$next" "$active"

Avant le remplacement, exécutez également un contrôle minimal propre au projet, par exemple en analysant le projet, en répertoriant les points d’entrée du build ou en lançant une série de tests rapides. Si le dépôt isolé contient des commits non publiés, ne supposez pas qu’un nouveau clone permettra de les récupérer. Utilisez les références et le reflog sauvegardés pour tenter de les lire dans une copie. Si les objets sont réellement absents, ils ne pourront être restaurés qu’à partir d’un serveur distant fiable, d’un autre clone complet ou des résultats de travail d’origine.

Intégrer la vérification des objets au processus de publication du cache

Un cache Git partagé ne doit pas être modifié simultanément par toutes les tâches. Un modèle plus robuste suit trois étapes : « générer, vérifier, publier ». Une tâche clone le nouveau cache dans un répertoire temporaire, exécute git fsck après la récupération, puis renomme le répertoire en nouvelle génération en lecture seule uniquement si la vérification réussit. Chaque tâche en cours reste attachée à sa propre génération et ne bascule pas vers une mise à jour publiée entre-temps.

Les contrôles courants peuvent se résumer à quatre règles : vérifier les objets avant de publier un cache ; séparer les espaces de travail des tâches de la base partagée ; conserver le hash de l’objet et la génération du cache en cas d’échec ; limiter les scripts de nettoyage à la suppression de générations complètes qui ne sont plus référencées. Ainsi, même si un téléchargement ou l’arrêt d’un processus produit une écriture incomplète, l’impact reste confiné au répertoire temporaire qui n’a pas encore été publié.

Enfin, formalisez la procédure de restauration dans un script exécutable et validez-la sur un dépôt de test dépourvu de données métier. Un exercice fiable doit au minimum démontrer les points suivants : un cache corrompu n’est plus distribué ; le commit cible peut être reconstruit depuis une source propre ; les modifications non validées disposent d’un chemin de sauvegarde indépendant ; le remplacement ne permet jamais à deux tâches d’écrire simultanément dans le même espace de travail.

Questions fréquentes

Faut-il supprimer immédiatement un fichier suspect dans .git/objects ?

Non. Il faut d’abord arrêter les écritures et conserver les journaux, les références et les changements locaux. Une suppression directe peut casser d’autres références et faire disparaître des indices.

La commande git gc peut-elle réparer une base d’objets corrompue ?

git gc n’est pas un outil de réparation. Elle réécrit et supprime des objets, ce qui peut masquer la cause. Pour un espace CI jetable, un clone propre puis vérifié est plus sûr.

Comment empêcher un cache Git endommagé de contaminer les tâches suivantes ?

Publiez des générations de cache immuables. Construisez chaque génération dans un répertoire séparé, validez-la avec git fsck, puis activez-la par renommage atomique.

Nœud physique dédié

Choisissez un Mac cloud pour vos builds continus

Comparez les configurations, les régions et les quatre cycles de facturation de SDKMac M4 et SDKMac M4 Pro, puis créez votre commande.

Choisir une formule de location