Aller au contenu
Réussir mon BTS SIO

Tape une notion, une commande ou le nom d'une épreuve.

Par exemple

Chapitre 214 minIntermédiaire

Les étapes d'un pipeline CI/CD

Par Josselyn C., prof de BTS SIO. Mis à jour le .

À la fin de ce chapitre, tu sauras

  • expliquer comment un pipeline décide qu'une étape a échoué ;
  • ordonner les étapes d'un pipeline, de la récupération du code à la production ;
  • installer des dépendances verrouillées avec la bonne commande ;
  • appliquer l'échec rapide et la règle « construire une fois, déployer partout ».

4 exercices corrigés1 schéma

Un pipeline CI/CD est la suite automatisée d'étapes qui transforme un commit en version vérifiée, puis livrée : récupérer le code, installer les dépendances, analyser, tester, construire, publier, déployer. Chaque étape doit réussir pour que la suivante démarre. Ce chapitre décrit ces étapes dans l'ordre, explique comment une machine sait qu'une étape a échoué, et donne les règles qui font un bon pipeline : rapide, reproductible et digne de confiance.

Le principe : des commandes et des codes de retour

Un pipeline ne fait rien de magique : il exécute des commandes, les mêmes que tu tapes dans ton terminal. Pour savoir si une commande a réussi, il lit son code de retour (exit code) :

  • 0 : la commande a réussi, l'étape est verte ;
  • tout autre nombre : la commande a échoué, l'étape est rouge et le pipeline s'arrête.
bash
npm test
echo $?        # affiche le code de retour de la dernière commande : 0 si tous les tests passent, 1 sinon

Jest, ESLint, PHPUnit, dotnet test, pytest : tous les outils de qualité renvoient un code différent de 0 quand ils trouvent un problème. C'est ce qui permet de les brancher dans n'importe quel pipeline.

Les étapes, dans l'ordre

N°ÉtapeExemple pour sorbier-fraisElle répond à la question
1DéclenchementUn push, une pull request, un tag, un horaireQuand vérifier ?
2Récupération du codeCopie du dépôt au commit exact sur une machine neuveQuel code vérifier ?
3InstallationInstaller Node.js 24, puis npm ciLes dépendances déclarées suffisent-elles ?
4Vérifications statiquesnpx prettier --check ., npm run lintLe code respecte-t-il les règles de l'équipe ?
5Tests unitairesnpm testChaque fonction fait-elle ce qu'elle doit ?
6Construction (build)Compiler, transpiler, minifier : npm run buildLe code se construit-il ?
7Tests d'intégrationL'API testée contre une vraie base MySQLLes composants fonctionnent-ils ensemble ?
8Analyse de sécuriténpm audit, analyse du code, recherche de secretsLe code ou ses dépendances ont-ils une faille connue ?
9EmpaquetageConstruire l'image Docker sorbier-frais:3f9c2e1Quel objet va-t-on livrer ?
10PublicationPousser l'image dans un registreOù l'objet est-il rangé ?
11Déploiement en recetteLancer l'image sur le serveur de recetteLa version tourne-t-elle dans un environnement réaliste ?
12Tests de fumée et d'acceptationcurl -f https://recette.sorbier.example/health, scénarios de recetteLe service répond-il vraiment ?
13Déploiement en productionMême image, après approbation ou automatiquementOn livre ?
14Vérification après déploiementSurveillance des erreurs et du temps de réponseLa production va-t-elle bien ?

Les étapes 1 à 8 forment l'intégration continue ; les étapes 9 à 14 la livraison ou le déploiement continu. Un projet de BTS commence par les étapes 1 à 5 et ajoute les autres au fil des besoins.

Intégrationcheckoutnpm cilinttests unitairesVérificationbuildtests + MySQLaudit sécuritéLivraisonimage Dockersha-3f9c2e1push registreDéploiementrecettetest de fuméeproductionÉchec du lint : le pipeline s’arrêteles étapes suivantes ne tournent pas, l’auteur est prévenucode de retour ≠ 0
Les étapes d'un pipeline CI/CD : au premier échec, tout s'arrête

Installer avec des dépendances verrouillées

La machine qui exécute le pipeline démarre vide. Elle doit installer exactement les mêmes versions de bibliothèques que sur le poste du développeur, sinon un test peut passer chez l'un et échouer chez l'autre. Le fichier de verrouillage (lockfile) fige ces versions : il doit être versionné dans Git.

ÉcosystèmeFichier de verrouillageCommande de CIÀ éviter dans la CI
Node.js (npm)package-lock.jsonnpm cinpm install, qui peut modifier le verrouillage
PHP (Composer)composer.lockcomposer install --no-interaction --no-progresscomposer update
Python (pip)requirements.txt avec versions exactespip install -r requirements.txtpip install sans version
.NET (NuGet)packages.lock.jsondotnet restore --locked-modedotnet restore sans verrouillage, si le projet en a un
Java (Maven)versions dans pom.xmlmvn -B verifyles versions LATEST ou les plages

npm ci (clean install) supprime node_modules, installe exactement ce que dit package-lock.json, et échoue si ce fichier ne correspond plus à package.json. C'est voulu : une dépendance ajoutée sans mettre à jour le verrouillage est détectée tout de suite.

Échouer vite : l'ordre des étapes

Un pipeline s'arrête au premier échec. On place donc en tête les étapes rapides et souvent en échec, et en fin les étapes lentes. C'est le principe d'échec rapide (fail fast) : le développeur apprend le problème en trente secondes plutôt qu'au bout de dix minutes, et la machine ne calcule pas pour rien.

ÉtapeDurée typique pour une petite APIPourquoi à cette place
Formatage et lint5 à 20 secondesTrès rapide, échoue souvent (une virgule, une variable inutilisée)
Tests unitaires10 secondes à 2 minutesRapides, ils couvrent la logique
Construction30 secondes à 3 minutesInutile de construire un code qui ne passe pas les tests
Tests d'intégration1 à 5 minutesIls démarrent une base, une API : plus lents
Tests de bout en bout5 à 20 minutesUn navigateur piloté, très lents : peu nombreux, souvent seulement avant la production

Deuxième levier : paralléliser. Le lint, les tests unitaires et l'analyse de sécurité ne dépendent pas les uns des autres. Placés dans trois jobs distincts, ils tournent en même temps sur trois machines, et la durée totale devient celle du plus long (les jobs parallèles).

Les vérifications statiques : formatage, lint, typage

Elles examinent le code sans l'exécuter.

  • Le formatage vérifie la mise en page : indentation, guillemets, longueur des lignes. Dans la CI, on vérifie sans modifier : npx prettier --check .. Le développeur corrige chez lui avec npx prettier --write ..
  • Le lint cherche les erreurs probables et les mauvaises pratiques : variable déclarée mais jamais utilisée, == au lieu de ===, await oublié. ESLint pour JavaScript, PHPStan ou Psalm pour PHP, les analyseurs Roslyn pour C#, Ruff pour Python.
  • Le typage vérifie la cohérence des types : npx tsc --noEmit pour TypeScript, PHPStan à un niveau élevé pour PHP, le compilateur pour C# et Java.
LangageFormatage (CI)Lint ou analyse statique
JavaScript, TypeScriptnpx prettier --check .npx eslint . (configuration eslint.config.js), npx tsc --noEmit
PHPvendor/bin/php-cs-fixer fix --dry-run --diffvendor/bin/phpstan analyse
C#dotnet format --verify-no-changesAvertissements du compilateur traités en erreurs (-warnaserror)
Pythonruff format --check .ruff check .

Construire et empaqueter : l'artefact

L'étape de construction produit un artefact : l'objet qui sera livré. Selon le projet, c'est un dossier dist/ de fichiers statiques, une archive .zip, un exécutable, un .jar, ou de plus en plus souvent une image Docker qui contient l'application et tout ce dont elle a besoin pour tourner.

Deux règles rendent la livraison fiable :

  1. Construire une seule fois, déployer partout (build once, deploy many). L'artefact testé en recette est exactement celui qui part en production. On ne reconstruit pas pour la production : une reconstruction pourrait tirer une dépendance différente, et l'on déploierait un objet jamais testé.
  2. Identifier chaque artefact sans ambiguïté. On l'étiquette avec l'empreinte du commit (3f9c2e1) ou un numéro de version (2.4.0), jamais seulement avec latest. Ainsi, on sait toujours quel code tourne en production, et on peut redéployer la version précédente en une commande (chapitre 12).

Déployer, puis vérifier

Le déploiement installe l'artefact dans un environnement : recette d'abord, production ensuite (chapitre 10). Un déploiement n'est pas terminé quand la commande rend la main, mais quand on a vérifié que le service répond. Le plus simple est un test de fumée (smoke test) : une requête sur une route de santé.

bash
curl --fail --silent --show-error --retry 5 --retry-delay 3 --retry-all-errors \
  https://recette.sorbier.example/health

--fail fait renvoyer à curl un code d'erreur si le serveur répond 4xx ou 5xx ; sans cette option, une page d'erreur 500 serait considérée comme un succès. --retry laisse au service le temps de démarrer.

Où tourne le pipeline : les runners

La machine qui exécute un pipeline s'appelle un runner (GitHub, GitLab) ou un agent (Jenkins, Azure Pipelines).

TypeDescriptionAvantagesInconvénients
Hébergé par la forgeUne machine virtuelle neuve fournie par GitHub ou GitLab, détruite après le jobRien à installer, toujours propre, sûreQuota de minutes sur les dépôts privés, pas d'accès direct à ton réseau interne
Auto-hébergé (self-hosted)Une machine à toi qui exécute les jobsAccès au réseau interne, matériel choisi, pas de quotaÀ maintenir et à sécuriser ; un job peut laisser des traces pour le suivant

Une machine neuve à chaque exécution est une force : aucun reste du job précédent, aucune configuration cachée. C'est aussi pourquoi chaque job commence par récupérer le code et installer ses outils.

Ce qu'on attend d'un bon pipeline

  • Reproductible : mêmes entrées, même résultat ; versions verrouillées, environnement neuf.
  • Rapide : moins de dix minutes pour une pull request.
  • Fiable : un test qui échoue une fois sur dix au hasard (flaky test) apprend à l'équipe à ignorer le rouge ; on le corrige ou on le met en quarantaine (les tests instables).
  • Bloquant : un échec empêche vraiment la fusion ou le déploiement (protection de branche).
  • Versionné : la description du pipeline vit dans le dépôt, avec le code, et passe en revue comme lui.
  • Sûr : droits minimaux, secrets protégés, actions tierces maîtrisées (chapitre 7).

Exercices

Exercice 1 sur 4 Remettre les étapes dans l'ordre Réussi À revoir

Voici les étapes du pipeline d'une application PHP, dans le désordre : vendor/bin/phpunit (tests unitaires), déploiement en production, composer install --no-interaction, récupération du code, vendor/bin/phpstan analyse, construction de l'image Docker, test de fumée en recette, publication de l'image dans le registre, déploiement en recette.

  1. Remets-les dans l'ordre.
  2. Lesquelles relèvent de l'intégration continue, lesquelles de la livraison ?
Voir la réponseMasquer la réponse de l'exercice 1
  1. Récupération du code ; composer install --no-interaction ; vendor/bin/phpstan analyse ; vendor/bin/phpunit ; construction de l'image Docker ; publication de l'image dans le registre ; déploiement en recette ; test de fumée en recette ; déploiement en production.
  2. Intégration continue : récupération, installation, analyse statique, tests. Livraison : construction de l'image, publication, déploiements et test de fumée.

PHPStan passe avant PHPUnit : l'analyse est plus rapide et détecte des erreurs qui feraient de toute façon échouer les tests.

Tu avais trouvé ?

Exercice 2 sur 4 Un pipeline toujours vert Réussi À revoir

Le pipeline de Hugo est toujours vert, même quand des tests échouent. Voici l'étape de test :

bash
npm test || echo "Des tests ont échoué, à regarder plus tard"
  1. Pourquoi l'étape est-elle verte ?
  2. Corrige-la.
Voir la réponseMasquer la réponse de l'exercice 2
  1. Quand npm test échoue (code 1), l'opérateur || exécute echo, qui réussit (code 0). Le code de retour de la ligne est celui de la dernière commande exécutée : 0, donc l'étape est verte.
  2. Il suffit d'écrire npm test. Si Hugo veut afficher un message, il doit conserver l'échec : npm test || { echo "Des tests ont échoué"; exit 1; }.

Tu avais trouvé ?

Exercice 3 sur 4 Gagner du temps Réussi À revoir

Le pipeline de sorbier-frais enchaîne quatre étapes dans un seul job : lint (1 min), tests unitaires (3 min), tests d'intégration (6 min), construction de l'image (2 min). L'installation des dépendances prend 1 minute au début du job.

  1. Quelle est la durée totale ?
  2. On sépare le lint, les tests unitaires et les tests d'intégration en trois jobs parallèles, chacun avec sa propre installation (1 min). La construction de l'image, dans un quatrième job qui attend les trois autres, a aussi besoin de l'installation. Quelle est la nouvelle durée ?
  3. Quel est l'intérêt de cette organisation quand le lint échoue ?
Voir la réponseMasquer la réponse de l'exercice 3
  1. 1 + 1 + 3 + 6 + 2 = 13 minutes.
  2. Les trois jobs parallèles durent 1 + 1 = 2 min, 1 + 3 = 4 min et 1 + 6 = 7 min : ils sont tous terminés au bout de 7 minutes. Le job de construction démarre alors et dure 1 + 2 = 3 min. Total : 7 + 3 = 10 minutes.
  3. Avec un seul job, un lint en échec arrête tout au bout de 2 minutes, mais on ne sait rien des tests. En parallèle, le lint échoue au bout de 2 minutes et les tests continuent : le développeur reçoit tous les problèmes en une seule exécution. La construction, elle, ne démarre pas, puisqu'elle attend les trois jobs.

Tu avais trouvé ?

Exercice 4 sur 4 Les bonnes commandes Réussi À revoir

Pour chaque projet, donne la commande d'installation des dépendances adaptée à la CI et la commande de vérification du formatage.

  1. Une API Node.js qui utilise Prettier.
  2. Une application Symfony qui utilise PHP-CS-Fixer.
  3. Une application WinForms en .NET 10.
Voir la réponseMasquer la réponse de l'exercice 4
  1. npm ci, puis npx prettier --check ..
  2. composer install --no-interaction --no-progress, puis vendor/bin/php-cs-fixer fix --dry-run --diff.
  3. dotnet restore (avec --locked-mode si le projet a un packages.lock.json), puis dotnet format --verify-no-changes. Une application WinForms ne se construit que sous Windows : le job tourne sur un runner Windows.

Tu avais trouvé ?

Chapitre terminé !

Fais les 4 exercices avant de passer à la suite : c'est en t'entraînant que tout se fixe.

chapitres
–
de série
–
du cours
–
Chapitre suivant : GitHub Actions : écrire un workflow YAMLRevoir le sommaire du cours