Un pipeline d'intégration continue qui refuse vraiment de livrer

CI/CD DevOps Tests Guide
Mis à jour le

Presque tout le monde finit par brancher une chaîne d’intégration continue. Peu de gens s’arrêtent sur la question qui décide de son utilité : qu’est-ce qui, ici, peut bloquer une livraison ?

Une chaîne qui lance les tests et laisse passer le reste donne un sentiment de sécurité sans la sécurité. Voici les cinq contrôles que je mets, ce qu’ils attrapent, et le prix de chacun.

1. Le typage, en entier

tsc --noEmit --incremental false

--incremental false n’est pas de la coquetterie. En mode incrémental, le cache peut masquer une erreur dans un fichier considéré comme inchangé, et la chaîne passe au vert sur du code qui ne compile pas ailleurs.

Ce contrôle attrape la plus grande part des régressions sur une base typée. Sur une réécriture mécanique de 1 200 chaînes de caractères récente, il a trouvé cinq fichiers cassés que la relecture n’avait pas vus, y compris un cas où une variable locale portait le même nom qu’une fonction injectée.

2. Les tests, sans exception tolérée

Un test ignoré est un test supprimé avec des étapes en plus. Si un test est instable, il faut le réparer ou l’enlever, pas le marquer.

Le piège courant : la chaîne exécute les tests mais n’échoue pas quand ils échouent, parce que la commande est enchaînée avec ; au lieu de &&, ou parce qu’un || true traîne. Vérifiez le code de sortie une fois, en cassant volontairement un test.

3. La cohérence de ce qui n’est pas du code

C’est la partie que les tutoriels sautent, et celle qui rapporte le plus.

Sur les projets que je tiens, la chaîne refuse de livrer quand un catalogue de traduction diverge entre les langues. Une clé présente en français et absente en anglais produit une chaîne brute chez le visiteur, et personne ne la voit avant qu’un anglophone tombe dessus. Elle refuse aussi quand une migration manque, c’est-à-dire quand un index est déclaré dans le schéma mais créé par aucune migration : il n’existe alors chez aucun client, et la lenteur qui en découle se manifeste des mois plus tard, loin de sa cause, sur une page qu’on ne pense pas à relier au problème. Le troisième cas est plus bête, un droit déclaré sans libellé, qui s’affiche vide dans la matrice de permissions.

Ces trois contrôles font une dizaine de lignes chacun et ont tous les trois déjà arrêté une livraison qui serait passée sans eux.

4. L’audit de dépendances, trié

npm audit --omit=dev dans la chaîne, oui. Faire échouer la chaîne sur son résultat brut, non : vous bloquerez sur des failles inatteignables et l’équipe prendra l’habitude de forcer le passage, ce qui coûte plus cher que de ne rien vérifier.

Le bon réglage est d’échouer sur les gravités critiques uniquement, et de produire le rapport complet en pièce jointe pour la relecture humaine.

5. La construction, dans les mêmes conditions que la production

Une construction qui réussit sur la machine de l’intégration continue et échoue dans l’image de production ne sert à rien. Si vous livrez en conteneur, la chaîne doit construire l’image, pas le projet.

C’est plus lent, entre trois et six minutes au lieu d’une. Le temps gagné à ne pas découvrir un module manquant au démarrage en production le rembourse au premier incident.

Déployer sans main humaine, jusqu’où

Le déploiement automatique après une fusion sur la branche principale convient à un site vitrine ou à une application interne. Il convient mal dès que la mise à jour touche une base de données appartenant à un client.

La règle que j’applique : automatique jusqu’à la préproduction, manuel au-delà, avec une trace de qui a déclenché. La différence de coût est d’un clic. La différence en cas d’erreur est celle entre un retour arrière tranquille et un appel un dimanche.

Une erreur que j’ai faite

Pendant des mois, ma chaîne était rouge sur un projet et je l’ai prise pour un caprice de l’exécuteur. C’était un vrai défaut : trois énumérations déclarées au mauvais endroit dans la carte des modules, donc retirées de la composition quand le module était inactif, ce qui produisait des énumérations sans aucune valeur que PostgreSQL refuse.

Une chaîne rouge qu’on ignore devient une chaîne qui ne sert plus. Si elle est rouge pour une mauvaise raison, il faut corriger la raison, pas s’habituer.