Aller au contenu
English
Compte : non connecté, synchronisation possible Mon apprentissage
Selectionner le thème
English

La CI de certcheck : six étapes, un run rouge, une correction

Par mis à jour le

Améliorer cette page

La CI de certcheck rejoue, à chaque modification poussée, les contrôles que vous lancez à la main sur votre poste, dans un ordre fixe, et s'arrête à la première étape qui échoue. Cette page est la consigne du lab qui précède l'épreuve de la formation : elle vous montre la pipeline réelle de certcheck, étape par étape, puis comment lire un run rouge (une exécution de la pipeline qui a échoué), le rejouer chez vous et le réparer. Elle ne remplace pas le cours sur le modèle d'une pipeline : lisez d'abord Qu'est-ce qu'une pipeline CI/CD ?, qui définit job, runner et étape.

  • Nommer les six étapes de la pipeline de certcheck et leur rôle
  • Comprendre pourquoi la première étape en échec arrête le job
  • Lire un journal de run rouge : quelle étape, quelle commande, quelle cause
  • Rejouer l'étape en échec sur votre poste, corriger, repousser
  • Rejouer toute la pipeline sans pousser, avec forgejo-runner exec (facultatif)
  • Avoir lu Qu'est-ce qu'une pipeline CI/CD ? (job, runner, étape) et suivi les leçons précédentes de la formation : tests avec pytest, ruff, Git et pull requests.
  • De la leçon partagée sur la pipeline, vous utilisez ici les parties sur le job et ses étapes, le runner, le journal, et la différence entre cache et artefact. Ses sections sur les environnements de déploiement relèvent de la formation Pipelines CI/CD, hors de cette initiation.
  • Pour faire le lab, une machine capable de faire tourner une machine virtuelle (voir « Mettre en pratique »). Sans elle, la lecture de la page suffit pour le modèle.
  • Pour rejouer la pipeline sur votre propre poste (facultatif) : un Linux x86-64 avec Docker, et le lab lancé (le dépôt à cloner vient de la forge de la VM). Dans la VM du lab, rien de plus n'est nécessaire.

Elle tient dans un seul fichier, .forgejo/workflows/ci.yml, qui déclare un déclencheur et un job de six étapes. Le déclencheur dit quand la forge la lance : à chaque push sur main et à chaque demande de fusion. Le job integrer s'exécute sur un runner (la machine qui joue les étapes) portant l'étiquette debian. Les étapes sont lues de haut en bas : regardez leur ordre, il compte.

name: ci
on:
push:
branches: [main]
pull_request:
permissions:
contents: read
jobs:
integrer:
runs-on: debian
steps:
- name: Récupérer le code
uses: https://data.forgejo.org/actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Préparer Python
run: uv python find ">=3.12"
- name: Installer les dépendances
run: uv sync --locked
- name: Qualité
run: |
uv run ruff check .
uv run ruff format --check .
- name: Tests
run: uv run pytest -v
- name: Construire le paquet
run: uv build

Le schéma résume l'enchaînement : six étapes en file, une seule issue verte, et une flèche en pointillés vers le run rouge quand une étape échoue (ici Qualité, pour un import inutile, comme dans le run lu plus bas).

Six étapes en ligne : récupérer le code, préparer Python, installer les dépendances, qualité avec ruff, tests avec pytest, construire le paquet, puis un run vert. Une flèche en pointillés part de l'étape qualité vers un run rouge, où le job est arrêté et les étapes suivantes ne sont pas exécutées.échec (ex. import inutile)1. Récupérer le code2. Préparer Python3. Installer les dépendances4. Qualité ruff5. Tests pytest6. Construire le paquetRun vertRun rouge : job arrêté,étapes suivantes non exécutées
Source · SVG
Échap pour fermer, + et − pour zoomer, défilement pour se déplacer. 100 %

Le modèle complet (déclencheurs, jobs, étapes, ce que le dépôt déclare et ce que la forge exécute) est expliqué dans Étapes et déclencheurs d'une pipeline CI/CD : cette page se contente de lire celle de certcheck.

Chaque étape rejoue un geste que vous connaissez déjà, et l'ordre va du moins cher au plus cher. Un défaut de forme se signale en quelques secondes ; il serait absurde d'attendre la fin des tests pour l'apprendre.

  1. Récupérer le code : le runner part d'une machine vide et clone le dépôt au commit poussé. Sans cela, rien à contrôler ; si elle échoue, rien ne peut suivre. L'option persist-credentials: false demande à l'action de ne pas enregistrer le jeton de la forge dans la configuration Git pour les étapes suivantes : la pipeline n'a pas besoin de repousser du code, elle ne garde donc aucun accès inutile.
  2. Préparer Python : uv python find ">=3.12" cherche un interpréteur Python 3.12 ou plus récent et affiche son chemin. Si aucun ne convient, la commande se termine en erreur (code de sortie non nul) : mieux vaut échouer ici, avec un message clair, que plus loin sur une erreur de syntaxe incompréhensible.
  3. Installer les dépendances : uv sync --locked installe exactement les versions du fichier de verrouillage. L'option --locked fait échouer l'étape si le verrou ne correspond plus à pyproject.toml : le runner ne « corrige » pas en silence, il vous dit que le projet est incohérent (voir Dépendances Python).
  4. Qualité : ruff check . cherche les erreurs que le linter sait trouver (import inutilisé, nom indéfini), ruff format --check . vérifie que le code est formaté comme le projet l'a décidé, sans rien modifier. C'est le travail mécanique de la revue, confié à la machine (voir Relire une pull request).
  5. Tests : pytest -v lance la suite et affiche chaque test. C'est l'étape qui dit si le comportement promis tient toujours (voir Premiers tests avec pytest).
  6. Construire le paquet : uv build fabrique le paquet installable. Si elle échoue alors que tout le reste passe, c'est l'emballage qui est cassé, pas le code.

Quand une étape échoue, la suivante ne s'exécute pas : le job s'arrête à la première étape en échec. On appelle cela échouer vite (fail fast). La conséquence se lit dans un journal : l'étape rouge est toujours la première cause à traiter, et les étapes grisées après elle ne sont pas des échecs supplémentaires, simplement des étapes qui n'ont pas eu lieu.

On lit le journal par la fin : la dernière étape exécutée est la coupable. On repère l'étape en échec, puis la commande qu'elle lançait, puis la ligne qui dit pourquoi. Dans le lab, le run de départ est rouge sur main : l'étape Qualité échoue à cause d'un import shutil que personne n'utilise dans src/certcheck/cli.py. Le journal du run, dans l'onglet Actions de la forge, montre cette sortie sous l'étape Qualité. C'est la sortie réelle de la commande uv run ruff check . sur ce dépôt (relevée avec ruff 0.17.0, code de sortie 1) :

F401 [*] `shutil` imported but unused
--> src/certcheck/cli.py:14:8
|
12 | import logging
13 | import os
14 | import shutil
| ^^^^^^
15 | import sys
16 | from pathlib import Path
|
help: Remove unused import: `shutil`
|
13 | import os
- import shutil
14 | import sys
|
Found 1 error.
[*] 1 fixable with `--fix`.

Lisez-le en partant du bas. Found 1 error. dit qu'il n'y a qu'une cause. Le message nomme la règle (F401), le fichier et la ligne (cli.py:14), et montre le code autour. Dans le journal complet de la forge, cette sortie est précédée de la ligne de l'étape Qualité, marquée en échec : c'est la dernière étape exécutée, et celles qui suivent (Tests, Construire le paquet) sont non exécutées, c'est le fail fast. Un code de sortie non nul (ici 1) signale l'échec, comme pour n'importe quelle commande Unix.

Remarquez ce que le journal ne dit pas : quel commit a introduit le défaut. Le message du dernier commit ne parle pas forcément du code, et l'historique Git est l'outil qui le retrouve. Même méthode de lecture que dans Étapes et déclencheurs d'une pipeline CI/CD, dont la section « Comment lire un échec et le reproduire en local ? » traite aussi le cas où l'échec ne se reproduit pas chez vous (version d'outil, variable d'environnement, fichier absent du dépôt).

Comment rejouer l'étape en local, puis corriger ?

Section intitulée « Comment rejouer l'étape en local, puis corriger ? »

Toute étape de la CI est une commande que vous pouvez lancer chez vous, dans le dépôt, et vous devez la rejouer avant de pousser. C'est tout l'intérêt d'une CI écrite avec des commandes ordinaires : le journal vous donne la commande, vous la tapez.

Fenêtre de terminal
uv sync --locked
uv run ruff check .

Vous obtenez le même message que sur la forge. Vous supprimez la ligne import shutil, vous relancez la même commande, puis vous rejouez les étapes suivantes (uv run ruff format --check ., uv run pytest -v, uv build) : corriger une étape peut révéler la suivante, que le fail fast vous cachait. Quand tout est vert chez vous, vous commitez et poussez : la forge relance la pipeline, et le run suivant doit être vert.

Le linter propose une réparation automatique (--fix). Elle est pratique pour un import inutilisé, mais relisez toujours la modification avant de la commiter : ce qu'une machine répare n'est pas forcément ce que vous vouliez.

Comment rejouer toute la pipeline sur sa machine ?

Section intitulée « Comment rejouer toute la pipeline sur sa machine ? »

Rejouer une commande montre une étape ; rejouer tout le fichier ci.yml montre le travail du runner, et forgejo-runner exec le fait sans rien pousser. Le fichier ci.yml s'appelle un workflow (la description de la pipeline). Le programme forgejo-runner sait le lire et en jouer les six étapes chez vous, dans l'ordre, avec le même fail fast que sur la forge : vous voyez naître le journal que vous lisiez dans l'onglet Actions. Ce geste est facultatif, le lab ne le note pas.

Dans la VM du lab, il n'y a rien à installer : le runner de la forge y est déjà, avec uv. L'option -i désigne l'image de conteneur dans laquelle jouer les étapes ; sa valeur spéciale -self-hosted veut dire « pas d'image : directement sur cette machine ». C'est ce que fait le runner de la forge avec son exécuteur host (la manière dont il lance les étapes) : même Python, même uv, mêmes versions de ruff et de pytest.

Fenêtre de terminal
cd ~/certcheck
forgejo-runner exec -W .forgejo/workflows/ci.yml -E push -i -self-hosted

L'option -W désigne le fichier de la pipeline, -E push l'événement simulé, c'est-à-dire ce qui déclenche la pipeline (ici un push). Sur le dépôt de départ, la sortie s'arrête au même endroit que le run rouge de la forge. Extrait relevé le 2026-10-14 avec forgejo-runner 13.2.0, pictogrammes retirés :

[ci/integrer] Run Main Récupérer le code
[ci/integrer] Success - Main Récupérer le code
[ci/integrer] Run Main Préparer Python
[ci/integrer] Success - Main Préparer Python
[ci/integrer] Run Main Installer les dépendances
[ci/integrer] | Using CPython 3.13.5 interpreter at: /usr/bin/python3
[ci/integrer] Success - Main Installer les dépendances
[ci/integrer] Run Main Qualité
[ci/integrer] | F401 [*] `shutil` imported but unused
[ci/integrer] | Found 1 error.
[ci/integrer] Failure - Main Qualité
[ci/integrer] Skipping step 'Tests' due to ''
[ci/integrer] Skipping step 'Construire le paquet' due to ''
[ci/integrer] Job failed
Error: Job 'integrer' failed

Le fail fast se lit en direct : Qualité échoue, Tests et Construire le paquet sont sautés (Skipping step), et la commande se termine avec le code de sortie 1. Une fois l'import retiré, le même appel joue les six étapes, affiche 5 passed sous Tests et se termine sur Job succeeded, code de sortie 0, en quelques secondes.

Sur votre propre poste, la même commande fonctionne si Docker est installé : exec crée alors un conteneur jetable qui tient lieu de machine du runner. Téléchargez dans votre dossier personnel le binaire de la même version que le lab, publié par Forgejo pour Linux x86-64, et vérifiez son empreinte SHA-256 avant de l'exécuter :

Fenêtre de terminal
cd ~
curl -fsSLO https://code.forgejo.org/forgejo/runner/releases/download/v13.2.0/forgejo-runner-13.2.0-linux-amd64
echo "fadaec897f5e6641c363f87ecaf75f866f99c81b7097ec6b433e4c48123fcad1 forgejo-runner-13.2.0-linux-amd64" | sha256sum -c
chmod +x forgejo-runner-13.2.0-linux-amd64

La commande sha256sum -c doit afficher forgejo-runner-13.2.0-linux-amd64: OK ; sinon, supprimez le fichier sans le lancer. Pour avoir le dépôt sur votre poste, ouvrez le tunnel (une redirection de port SSH, décrite dans « Mettre en pratique ») et gardez-le ouvert pendant le clone : sans lui, git clone échoue sur Failed to connect to localhost port 3000. Dans ce clone, -i reçoit cette fois une image (le modèle de machine dont le conteneur est créé) : l'image officielle d'uv, avec uv 0.12.21 comme le lab et Python 3.13, épinglée par digest (l'empreinte après @sha256: garantit que c'est exactement cette image et pas une autre du même nom).

Fenêtre de terminal
git clone http://localhost:3000/apprenant/certcheck.git
cd certcheck
~/forgejo-runner-13.2.0-linux-amd64 exec -W .forgejo/workflows/ci.yml -E push \
-i ghcr.io/astral-sh/uv:0.12.21-python3.13-trixie@sha256:2978ff8ce5196e8c5d3eed7c965bbda0294dde49779715106fa9da453601b295

Le premier appel télécharge l'image (7 secondes sur notre connexion, run compris, davantage sur une ligne lente), les suivants ont pris 3 à 5 secondes, et le conteneur est supprimé à la fin. La sortie est celle de la VM, à quelques détails près : l'interpréteur est le Python 3.13.15 de l'image (/usr/local/bin/python3) et uv affiche le téléchargement de ruff et de pytest. Pour faire place nette ensuite, supprimez l'image avec docker image rm suivi de son nom complet, et le binaire avec rm ~/forgejo-runner-13.2.0-linux-amd64.

Un run local ne remplace pas le run de la forge, et ses écarts se connaissent. C'est le run de la forge qui fait foi, et c'est lui que la validation du lab lit :

  • ce qui est joué : exec copie votre arbre de travail (les fichiers du dossier tels qu'ils sont maintenant), modifications non commitées comprises (hors fichiers listés dans .gitignore), alors que la forge clone le commit poussé. Un run local vert ne dit rien d'un fichier oublié dans le commit ;
  • l'étape Récupérer le code n'exécute pas l'action de checkout : exec la remplace par cette copie ;
  • les secrets de la forge ne sont pas transmis ; la pipeline de certcheck n'en utilise aucun ;
  • dans un conteneur, uv retélécharge ruff et pytest à chaque run, car le conteneur repart de zéro : il faut un accès réseau.

Et act ? Le guide act présente cet outil, conçu pour les workflows GitHub Actions : sa version 0.2.89 refuse ce fichier tel quel, car il n'accepte pas une action désignée par URL complète (uses: https://...), syntaxe propre à Forgejo. Le runner de Forgejo, lui, joue le fichier que la forge joue.

Le lab vous remet un dépôt certcheck dont main est rouge, et vous fait réparer ce run, puis provoquer vous-même deux échecs pour voir comment la CI les signale. Vous lisez le run de départ, vous trouvez l'étape et la cause, vous corrigez. Ensuite, dans une demande de fusion, vous cassez volontairement un test, observez le run rouge, le réparez par un second commit vert ; puis vous refaites la même chose avec le formatage. Vous conservez aussi dans un fichier la sortie d'une commande de la pipeline rejouée en local sur un commit rouge, et vous répondez dans un fichier YAML : l'ordre des étapes, celle qui installe les dépendances, celle qui a échoué sur le premier run.

La validation lit l'état de la forge (statuts des runs, demandes de fusion, commits) et de la machine, jamais les commandes tapées. La pipeline fournie ne change pas : on la lit, on ne la réécrit pas.

Ce lab a besoin d'une machine virtuelle : elle héberge une forge Forgejo et son runner, qui jouent la pipeline pour de vrai. Il se lance avec DSOXLab : dsoxlab provision crée la VM (sous KVM ou Incus), puis dsoxlab run ci-certcheck pose l'état de départ et dsoxlab check ci-certcheck note le résultat. Si vous n'avez pas de quoi faire tourner une VM, la lecture de cette page et du journal ci-dessus vous donne le modèle ; le geste, lui, ne se prouve qu'avec le lab.

Pour ouvrir la forge dans votre navigateur, créez un tunnel avec ssh -L 3000:localhost:3000 forge-1.lab, puis ouvrez http://localhost:3000 : l'onglet Actions du dépôt liste les runs, et le bouton de nouvelle demande de fusion sert aux deux échecs provoqués. Le challenge du lab donne les identifiants du compte. L'installation de DSOXLab est décrite dans la documentation DSOXLab.

Ce lab s'arrête à la CI : ni livraison, ni déploiement, ni promotion. Ces sujets relèvent de la formation Pipelines CI/CD, qui utilise la même forge mais un autre projet fil rouge, inventaire : elle ne reprend pas le dépôt certcheck.

  • La pipeline de certcheck rejoue six étapes dans l'ordre : récupérer le code, préparer Python, installer les dépendances, qualité, tests, construire le paquet.
  • La première étape en échec arrête le job : c'est le fail fast, et les étapes suivantes sont « non exécutées », pas fautives.
  • Un journal se lit par la fin : étape rouge, commande lancée, ligne qui donne la cause.
  • Une étape de CI est une commande ordinaire : on la rejoue en local avant de pousser.
  • Corriger une étape peut révéler la suivante : on rejoue toute la chaîne.
  • forgejo-runner exec rejoue toute la pipeline sans pousser, mais il joue l'arbre de travail : le run de la forge, sur le commit poussé, reste celui qui fait foi.

Ce projet vous est utile ?

Les guides, formations, quiz et labs restent gratuits. Si vous les utilisez régulièrement, vous pouvez contribuer à leur maintenance et à leur développement.

Devenir membre de soutien (nouvel onglet) Soutenir une fois (nouvel onglet) En savoir plus

Suivez aussi le projet sur LinkedIn