Aller au contenu

Note d'ingénierie

CI/CD avec GitHub Actions : le pipeline qui attrape les erreurs avant la production

Par Alexandre Kaczor4 min de lecture
  • DevOps
  • CI/CD
  • GitHub Actions
  • Docker
Illustration de l'article « CI/CD avec GitHub Actions : le pipeline qui attrape les erreurs avant la production »

Un pipeline se juge à ce qu'il empêche d'arriver en production, pas à son nombre d'étapes. Leur ordre, le cache qui change tout, le déploiement sans casse.

Un pipeline se juge sur une seule question : qu'est-ce qu'il empêche d'atteindre la production ? Un pipeline vert qui laisse passer une migration non appliquée ou une variable de build vide n'a pas rendu service, il a donné confiance à tort — ce qui est pire que pas de pipeline du tout.

L'ordre des étapes est une décision économique

Les étapes doivent être classées par coût croissant et probabilité d'échec décroissante. Une erreur de typage doit tomber en quarante secondes, pas après huit minutes de construction d'image Docker.

lint + typecheck      →  ~1 min   ← échoue le plus souvent
tests unitaires       →  ~2 min
tests d'intégration   →  ~5 min
build des images      →  ~8 min
déploiement           →  ~2 min   ← échoue le plus rarement

Le corollaire compte autant : les étapes indépendantes doivent tourner en parallèle. Lint et tests unitaires n'ont aucune raison de s'attendre.

Le cache, ou le pipeline que personne n'attend

Un pipeline de douze minutes n'est pas seulement lent : il change les comportements. On pousse moins souvent, on regroupe les changements, on fusionne sans attendre la fin. Le cache n'est donc pas un confort, c'est ce qui maintient l'usage.

- uses: pnpm/action-setup@v4
  with: { version: 9 }
- uses: actions/setup-node@v4
  with:
    node-version: 22
    cache: pnpm        # cache le store pnpm, pas node_modules
- run: pnpm install --frozen-lockfile

Deux points souvent manqués :

  • --frozen-lockfile — sans lui, l'installation peut modifier le fichier de verrouillage, et la CI ne teste alors plus les mêmes versions que celles qui partiront en production. C'est le genre d'écart qui produit un bug « impossible à reproduire ».
  • Mettre en cache le store, pas node_modules — restaurer un node_modules construit sur une autre plateforme ou une autre version de Node fabrique des pannes très difficiles à lire.

Ce qui manque à la plupart des pipelines

Trois vérifications rarement présentes, qui attrapent des erreurs que les tests ne voient pas.

La construction dans les conditions de production

Les tests tournent en mode développement. Or plusieurs classes d'erreurs n'apparaissent qu'au build de production : arbre mort mal éliminé, incompatibilité de rendu serveur, et surtout variables de build absentes — celles qui, sur Next.js ou Vite, sont inlinées à vide sans le moindre message. Une image construite avec les mêmes arguments qu'en production est la seule façon de le savoir avant les utilisateurs.

Les migrations rejouées sur une base vierge

Une migration qui s'applique sur votre base ne prouve rien : elle prouve qu'elle s'applique sur un état particulier. Rejouer la suite complète depuis zéro, dans un service Postgres éphémère, révèle les dépendances implicites à un état local — ainsi que les migrations qui n'ont jamais été commitées.

L'audit de dépendances, avec un seuil

À bloquer sur les vulnérabilités hautes et critiques seulement. Bloquer sur tout garantit qu'au bout de trois semaines l'étape sera désactivée, ce qui laisse moins de sécurité qu'un seuil raisonnable respecté.

Déployer sans casser

Le déploiement est l'étape où un pipeline peut faire des dégâts réels. Trois garde-fous :

Une sauvegarde avant toute migration. Dans le job de déploiement, avant d'appliquer quoi que ce soit. Sans exception, y compris « pour une petite colonne ».

Un environnement GitHub protégé pour la production, avec les secrets qui lui sont propres. Cela évite qu'une branche de test puisse déployer, et donne un point de validation manuelle quand on le souhaite.

Une vérification post-déploiement qui échoue. C'est le garde-fou le plus souvent oublié : le pipeline doit vérifier de l'extérieur que le service répond, et passer au rouge sinon.

- name: Vérification de santé
  run: |
    for i in {1..10}; do
      code=$(curl -s -o /dev/null -w "%{http_code}" https://api.domaine/api/v1/health/ready)
      [ "$code" = "200" ] && exit 0
      sleep 6
    done
    exit 1

La sonde doit viser un point de terminaison qui touche réellement la base et le cache. Interroger la page d'accueil ne prouve que la survie du serveur web : elle répond parfaitement quand la base est morte.

Une chose à ne pas mettre en CI

Les secrets de production dans des workflows déclenchés par pull_request. Une pull request venue d'un fork exécute du code que vous n'avez pas relu ; si le workflow a accès aux secrets, ce code aussi. GitHub distingue précisément ces deux déclencheurs pour cette raison : le déploiement appartient à push sur la branche principale, jamais à l'ouverture d'une PR.