Aller au contenu
English

Configurer l'exécuteur

  • Leçon 4
  • 45 min
  • Module 2 : Créer un pipeline CI reproductible
Leçon 4 sur 15 · CI/CD, tests et mises en production sûresConfigurer l'exécuteur

Ce que vous saurez faire

Une vérification qui s’exécute seulement sur votre ordinateur dépend de cet ordinateur. Donnez aux vérifications une machine propre au début de chaque tâche. Ainsi, un résultat a le même sens pour toute l’équipe.

Avant de commencerCe qu’il faut savoir et avoir sous la mainEnviron 45 minutes

Ce que vous devez savoir

Ce qu’il vous faut sur votre ordinateur

  • Le dossier de projet ~/projects/ticket-api-ci (Windows : $HOME\projects\ticket-api-ci) contenant l’API de billets 1.1.0 du cours Docker et le commit de docs/release-process.md, provenant de Définir les vérifications : Exercice du module. C’est un dépôt Git sur la branche main, sans dépôt distant pour le moment.
  • Son environnement virtuel .venv, créé à partir de requirements-lock.txt, comme dans Environnement et premier point de terminaison : étape 1 : créer le projet du cours sur les API. Le clone du module 1 n’a pas de .venv : créez-le dans ~/projects/ticket-api-ci.
  • Un compte GitHub gratuit. Vous créez un dépôt public dans cette leçon. Les dépôts publics exécutent gratuitement GitHub Actions sur les exécuteurs standard de GitHub.
  • Git. L’interface en ligne de commande GitHub gh est facultative : chaque étape GitHub est aussi décrite pour les pages Web.
  • Si votre projet manque ou ne fonctionne plus, copiez l’instantané end-of-m01, comme dans Utiliser un instantané.

Exécuté avec GitHub Actions (exécuteurs ubuntu-24.04) dans le dépôt public Nextia-Intelligence/ticket-api-ci, et ruff 0.16.10 avec Python 3.14 sur macOS (Apple silicon). Les étapes Windows et Linux ne sont pas testées.

Les vérifications d’Amira fonctionnent sur son Mac. Sam pose une question pertinente : « Est-ce qu’elles fonctionnent aussi sur un ordinateur qui n’est pas le tien? » Son Mac contient des paquets d’anciennes expériences, un cache de la semaine dernière et des réglages que personne n’a notés. Une vérification qui réussit sur ce Mac prouve seulement qu’elle y réussit.

Un exécuteur est la machine qui exécute les tâches d’un flux de travail. GitHub en propose deux types :

Exécuteur hébergé par GitHub Exécuteur autohébergé
Qui possède la machine GitHub Vous : un serveur, une machine virtuelle ou un ordinateur dans votre bureau
État au début d’une tâche Une nouvelle machine virtuelle (VM) Ce que la dernière tâche y a laissé
Coût pour un dépôt public Gratuit sur les exécuteurs standard Votre propre matériel et son entretien
À utiliser pour La plupart des pipelines, et tout ce cours Du matériel spécialisé (un GPU), ou l’accès à un réseau privé

Le cours utilise seulement des exécuteurs hébergés par GitHub. Une ligne du flux de travail choisit l’exécuteur : runs-on: ubuntu-24.04.

« À l’exception des exécuteurs à un seul processeur, chaque exécuteur hébergé par GitHub est une nouvelle machine virtuelle (VM) hébergée par GitHub » (Documentation GitHub). Le journal de la tâche indique quelle machine elle a reçue. Voici le début de la première tâche de l’exécution 37663364319 dans le dépôt de référence :

Current runner version: '2.337.0'
Azure Region: southcentralus
Operating System
Ubuntu
24.04.5
LTS
Runner Image
Image: ubuntu-24.04
Version: 20260927.320.1
GITHUB_TOKEN Permissions
Contents: read
Metadata: read

La deuxième tâche de cette exécution a démarré environ 6 secondes après la fin de la première. Elle s’est exécutée sur une autre VM, dans centralus. Rien de la première VM ne s’y trouvait. Elle a récupéré le code à nouveau.

C’est l’intérêt d’un exécuteur propre. Chaque tâche démarre à partir de la même image connue (ici ubuntu-24.04, version 20260927.320.1). Elle reçoit seulement ce que le flux de travail installe. Si une vérification nécessite un paquet absent du fichier de verrouillage, la tâche échoue, à chaque exécution, pour tout le monde. Une étape de configuration manquante ne peut pas passer inaperçue sur un exécuteur.

Deux cases côte à côte. La tâche 1, analyse statique, tests et audit, s'exécute sur une nouvelle VM avec Ubuntu 24.04.5, supprimée après la tâche : récupération de c862935, Python 3.14.8, pip install, puis ruff, pytest et pip-audit, qui écrivent reports/junit.xml. Une flèche portant l'étiquette needs : checks, seul le résultat passe, mène à la tâche 2, construction, test de bon fonctionnement et envoi, sur une autre nouvelle VM où rien de la tâche 1 n'est présent : nouvelle récupération de c862935, docker build linux/amd64, puis test de bon fonctionnement, envoi et build-info.json. Sous les deux, une case en pointillés : stocké par GitHub, hors des VM. Elle contient le cache pip (44 Mo, clé créée à partir de l'empreinte de requirements-lock.txt; accélère l'exécution sans jamais changer les résultats), les artefacts reports (junit.xml, 773 octets, conservés 90 jours), le cache des couches BuildKit (type=gha) et l'artefact build-info (conservé 90 jours).
Chaque tâche reçoit une nouvelle VM, supprimée après la tâche. Seul ce que le flux de travail sauvegarde hors de la VM reste : les caches accélèrent la prochaine exécution, et les artefacts conservent les résultats.
Description textuelle du diagramme

La tâche 1 exécute les vérifications sur une nouvelle VM. La tâche 2 construit l’image sur une autre nouvelle VM et doit récupérer le code à nouveau. Rien de la tâche 1 ne s’y trouve. La flèche entre elles transmet seulement le résultat : la tâche 2 démarre uniquement si la tâche 1 a réussi. Deux éléments hors des VM restent après une tâche. Un cache conserve des fichiers qui accélèrent la prochaine exécution, comme les paquets téléchargés. Un artefact conserve un résultat pour les personnes et les tâches suivantes, comme le rapport de tests. La leçon 3 de ce module porte sur les artefacts.

Une VM propre a un coût : chaque tâche télécharge tout à nouveau. Un cache est un ensemble de fichiers qu’une tâche sauvegarde et qu’une tâche ultérieure restaure pour éviter de les télécharger à nouveau. Le flux de travail demande à actions/setup-python de mettre en cache les téléchargements de pip :

.github/workflows/ci.yml (part)
- uses: actions/setup-python@v7
with:
python-version: "3.14.8"
cache: pip
cache-dependency-path: requirements-lock.txt

Le cache possède une clé : un nom formé à partir du système d’exploitation de l’exécuteur, de la version de Python et d’une empreinte de requirements-lock.txt. Une tâche restaure un cache seulement si la clé correspond. Les vrais journaux montrent les trois cas.

La première exécution réussie n’a trouvé aucun cache, a effectué l’installation en 15 secondes et en a sauvegardé un à la fin de la tâche :

pip cache is not found
...
Sent 44025642 of 44025642 (100.0%), 42.1 MBs/sec
Cache saved with the key: setup-python-Linux-x64-24.04-Ubuntu-python-3.14.8-pip-ec6bc0c05328f7a2f23375d7193327a08c67ec754519aba7be011c58d10a258d

L’exécution suivante (37664331105, la première demande de tirage) avait le même fichier de verrouillage. La clé correspondait donc :

Cache restored successfully
Cache restored from key: setup-python-Linux-x64-24.04-Ubuntu-python-3.14.8-pip-ec6bc0c05328f7a2f23...

L’installation a pris 12,3 secondes au lieu de 15,0 secondes. Le gain est faible, car pip installe quand même chaque paquet. Il évite seulement les téléchargements. Le troisième cas est moins évident : la toute première exécution a échoué et n’a sauvegardé aucun cache. Le cache est sauvegardé dans une étape « post » à la fin de la tâche. Cette étape ne s’est pas exécutée après l’échec.

Voici la règle. Un cache peut accélérer une tâche. Il ne doit jamais changer ce que la tâche vérifie ou construit. Ce cache respecte la règle pour trois raisons :

  • La clé provient du fichier de verrouillage. Si une version dans requirements-lock.txt change, l’empreinte change. L’ancien cache ne correspond plus, et la tâche télécharge à nouveau.
  • pip installe exactement les versions du fichier de verrouillage. Le cache lui fournit seulement les mêmes fichiers plus rapidement.
  • L’absence de cache ralentit seulement la tâche. Le résultat reste le même.

Un mauvais cache enfreint la règle. Par exemple, un cache du dossier .venv entier avec une clé fixe, comme venv, restaurerait d’anciens paquets après une modification du fichier de verrouillage. Les vérifications testeraient alors des paquets que l’image ne contient pas. La leçon 2 utilise la même idée pour la construction Docker, avec le cache des couches de BuildKit (type=gha). Comme dans le cours Docker, un fichier modifié déclenche à nouveau son étape.

GitHub supprime un cache que personne n’a utilisé pendant 7 jours. Un dépôt peut conserver 10 Go de caches par défaut. Une tâche peut restaurer les caches de sa propre branche et de la branche par défaut, main. (Source : Documentation GitHub : mise en cache des dépendances.)

Le flux de travail utilise les actions par version majeure : actions/checkout@v7. Le propriétaire de l’action peut déplacer v7 vers une version v7.x plus récente contenant des correctifs. Le journal montre le commit exact que v7 désignait ce jour-là :

Download action repository 'actions/checkout@v7' (SHA:3d3c42e5aac5ba805825da76410c181273ba90b1)
Download action repository 'actions/setup-python@v7' (SHA:5fda3b95a4ea91299a34e894583c3862153e4b97)

La référence la plus stricte est ce SHA de commit complet : uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1. GitHub précise que c’est « actuellement la seule façon d’utiliser une action comme une version immuable » (Documentation GitHub : utilisation sécurisée). Le compromis est le même qu’avec l’empreinte d’une image dans le cours Docker : rien ne change sans votre intervention. Cela inclut les correctifs de sécurité. Ce cours fixe la version majeure, car toutes les actions utilisées ici proviennent de GitHub ou de Docker. Pour une action provenant d’une personne ou d’un petit projet, fixez le SHA et lisez le code de cette version.

Amira crée le dépôt, envoie le projet et ajoute le premier flux de travail. Elle s’attend à une réussite : les cours sur les API et sur Docker avaient 25 tests réussis.

Sur github.com, Amira sélectionne + (en haut à droite) → New repository (nouveau dépôt). Elle entre le nom ticket-api-ci, choisit Public (public) et laisse les options README, .gitignore et licence vides. Le projet possède déjà ses fichiers. Elle sélectionne Create repository (créer le dépôt).

Terminal, in ~/projects/ticket-api-ci
cat .gitignore
git status
git remote add origin https://github.com/<you>/ticket-api-ci.git
git push -u origin main

Remplacez <you> par votre nom d’utilisateur GitHub. Si Git affiche remote origin already exists (le dépôt distant origin existe déjà), le dossier pointe encore vers celui du cours Docker. Exécutez git remote remove origin, comme dans Définir les vérifications : Exercice du module, puis ajoutez à nouveau le dépôt distant. git push envoie tous les commits de main, y compris celui contenant docs/release-process.md. Si Git demande un mot de passe, votre mot de passe GitHub ne fonctionne pas. Connectez-vous d’abord avec gh auth login, ou utilisez une autre méthode de la documentation GitHub.

3. Ajouter les outils et les réglages d’analyse statique

Section intitulée « 3. Ajouter les outils et les réglages d’analyse statique »

Deux nouveaux outils vont dans requirements.txt, dans la section pour travailler sur l’API : ruff, un analyseur statique rapide pour Python, et pip-audit, utilisé dans Construire et tester.

requirements.txt (the end)
# Needed to work on the API
pytest==9.1.1
httpx2==2.13.1
ruff==0.16.10
pip-audit==2.10.1

Installez-les et réécrivez le fichier de verrouillage, comme dans le cours sur les API. Dans le dépôt de référence, cela a ajouté 18 lignes, comme ruff==0.16.10, pip_audit==2.10.1 et leurs dépendances.

Terminal, with .venv active
python -m pip install -r requirements.txt
python -m pip freeze > requirements-lock.txt

Indiquez ensuite à ruff ce qu’il doit vérifier, dans pyproject.toml :

pyproject.toml (add at the end)
[tool.ruff]
line-length = 100
target-version = "py312"
[tool.ruff.lint]
select = ["E", "F", "I", "B"]

E et F correspondent aux erreurs de base : erreurs de style et véritables erreurs, comme une importation inutilisée. I vérifie l’ordre des importations. B détecte les bogues probables. Le cours n’utilise pas ruff format. Cet outil modifierait le code du cours sur les API uniquement pour le style. L’historique de chaque modification serait alors plus difficile à lire.

GitHub exécute chaque fichier YAML du dossier .github/workflows/. Amira crée .github/workflows/ci.yml. Le dépôt de référence a ajouté le pipeline entier dans un seul commit. Cette leçon commence avec la première tâche et une seule vérification. Les deux leçons suivantes ajoutent le reste :

.github/workflows/ci.yml
# Continuous integration: check every change, then build one image per commit.
# Pull requests: checks and a test build (nothing is pushed).
# main, hotfix/* and version tags: checks, then build, smoke-test and push the
# image to GitHub Container Registry, with build evidence.
name: CI
on:
pull_request:
push:
branches: [main, "hotfix/**"]
tags: ["v*"]
permissions:
contents: read
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
jobs:
checks:
name: Lint, test, audit
runs-on: ubuntu-24.04
timeout-minutes: 15
steps:
- uses: actions/checkout@v7
- uses: actions/setup-python@v7
with:
python-version: "3.14.8"
cache: pip
cache-dependency-path: requirements-lock.txt
- name: Install the exact versions
run: python -m pip install -r requirements-lock.txt
- name: Lint
run: ruff check .

Chaque partie a sa raison d’être :

  • on: énumère les déclencheurs. Chaque demande de tirage exécute les vérifications. Un envoi les exécute sur main, sur une branche de correctif urgent et sur une étiquette de version. Le module 3 retire le déclencheur d’étiquette quand le flux de mise en production prend le relais.
  • permissions: contents: read limite GITHUB_TOKEN, la clé fournie par GitHub à chaque exécution, à la lecture du code. Une tâche qui a besoin de plus de droits les demande. Les lignes GITHUB_TOKEN Permissions (autorisations de GITHUB_TOKEN) du journal montrent le résultat.
  • concurrency regroupe les exécutions d’une même branche. Un nouvel envoi à une demande de tirage annule son exécution précédente. Sur main, cancel-in-progress vaut false. Chaque commit de main obtient donc une exécution complète.
  • name: Lint, test, audit est le nom de la tâche sur GitHub. La leçon 3 fait de ce nom une vérification obligatoire. Choisissez-le donc avec soin.
  • timeout-minutes: 15 arrête une tâche qui reste bloquée. Le délai par défaut est beaucoup plus long.
  • Les étapes : récupérer le code, obtenir Python 3.14.8 avec le cache pip, installer les versions exactes, effectuer l’analyse statique.

Dans les expressions, ${{ github.ref }} est la branche ou l’étiquette de l’exécution. ${{ github.event_name }} est le déclencheur, comme push.

Terminal
git add requirements.txt requirements-lock.txt pyproject.toml .github/workflows/ci.yml
git commit -m "Add the CI pipeline: the first job, with lint"
git push

Faites une prédiction : les cours sur les API et sur Docker ont testé ce code. La tâche réussit-elle?

Sur GitHub, ouvrez l’onglet Actions (actions) du dépôt. L’exécution apparaît en quelques secondes. Dans le dépôt de référence, la première exécution 37663263329 a échoué après 25 secondes :

La page de l'exécution CI 1, Add the CI pipeline (ajouter le pipeline CI), avec l'état Failure (échec), une durée totale de 29 secondes, aucun artefact, déclenchée par l'envoi du commit 399c0d2 à main. Le graphe des tâches montre Lint, test, audit (analyse statique, tests, audit) avec une croix rouge après 25 secondes, reliée à Build, smoke-test and push the image (construire, tester le bon fonctionnement et envoyer l'image), ignorée après 0 seconde. Annotations : 1 erreur et 1 avertissement. L'erreur, Lint, test, audit : Process completed with exit code 1 (processus terminé avec le code de sortie 1). L'avertissement, Lint, test, audit : No files were found with the provided path : reports/. No artifacts will be uploaded. (Aucun fichier trouvé au chemin fourni : reports/. Aucun artefact ne sera téléversé.)
La première exécution du dépôt de référence, telle qu'un visiteur non connecté la voit. Son fichier contenait déjà la deuxième tâche, que GitHub a ignorée parce que la première avait échoué. Votre première exécution comporte une seule tâche. L’image est en anglais.

La page indique que la tâche a échoué, mais pas pourquoi. Pour lire le journal, connectez-vous, ouvrez la tâche, puis ouvrez l’étape rouge Lint (analyse statique). Ou utilisez l’interface en ligne de commande GitHub :

Terminal
gh run view --log-failed

La sortie (abrégée; exécution de référence) :

B904 Within an `except` clause, raise exceptions with `raise ... from err` or `raise ... from None` to distinguish them from errors in exception handling
--> ticket_api/config.py:28:13
|
27 | except OSError as error:
28 | raise SettingsError(f"{name}_FILE: cannot read {path}: {error.strerror}")
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
29 | return os.environ.get(name) or None
|
I001 [*] Import block is un-sorted or un-formatted
--> ticket_api/routes.py:1:1
...
Found 2 errors.
[*] 1 fixable with the `--fix` option.
##[error]Process completed with exit code 1.

Le code fonctionne et ses tests réussissent. Mais l’analyseur statique a trouvé deux problèmes qu’aucun test ne cherchait. Les tests ont vérifié ce que le code fait. L’analyseur statique a vérifié comment il est écrit.

Un échec en CI est plus facile à corriger quand vous pouvez le voir sur votre propre ordinateur. Le fichier de verrouillage a fourni ruff 0.16.10 à l’exécuteur. Il vous a fourni la même version :

Terminal, with .venv active
ruff --version
ruff check .

La sortie : ruff 0.16.10, puis les deux mêmes erreurs, ligne pour ligne, et Found 2 errors. (2 erreurs trouvées). Le code de sortie est 1, comme sur l’exécuteur. Même outil, même version, mêmes réglages, même résultat. Voilà pourquoi la tâche installe les paquets à partir du fichier de verrouillage.

I001 peut être corrigé automatiquement. ruff check --fix . trie les importations de ticket_api/routes.py :

ticket_api/routes.py
import asyncio
from typing import Annotated, get_args
import logging
from typing import Annotated, get_args

B904 exige une décision, donc ruff ne le corrige pas. Dans un bloc except, une nouvelle erreur entraîne l’ancienne avec elle, et le journal affiche les deux. Ici, SettingsError indique déjà le problème en une ligne claire. L’ancien OSError ajoute donc seulement de l’information inutile. from None indique que ce choix est volontaire :

ticket_api/config.py
except OSError as error:
raise SettingsError(f"{name}_FILE: cannot read {path}: {error.strerror}")
raise SettingsError(f"{name}_FILE: cannot read {path}: {error.strerror}") from None

Vérifiez à nouveau, puis créez un commit et envoyez-le :

Terminal
ruff check .
python -m pytest -q
git commit -am "Fix the two lint findings: raise ... from None, and sort the imports"
git push

La sortie des deux premières commandes :

All checks passed!
......................... [100%]
25 passed in 0.25s

La nouvelle exécution réussit. L’étape Lint de l’exécution 37663364319 du dépôt de référence a affiché All checks passed! (toutes les vérifications ont réussi; même message). Son commit est c862935 : deux lignes modifiées.

Exercice guidé : votre exécuteur et votre première exécution réussie

Section intitulée « Exercice guidé : votre exécuteur et votre première exécution réussie »
  1. Créez le dépôt public ticket-api-ci sur GitHub, sans README.
  2. Vérifiez .gitignore et git status, puis poussez main.
  3. Ajoutez ruff==0.16.10 et pip-audit==2.10.1, installez-les, puis régénérez le fichier de verrouillage.
  4. Ajoutez les paramètres [tool.ruff] à pyproject.toml.
  5. Créez .github/workflows/ci.yml avec la première tâche, puis faites un commit (enregistrement des changements) et poussez-le.
  6. Ouvrez l’exécution dans l’onglet Actions (actions). Notez la version de l’image de l’exécuteur et la région Azure dans Set up job (préparer la tâche).
  7. Lisez le journal de l’étape qui a échoué. Ensuite, exécutez ruff check . sur votre ordinateur et comparez.
  8. Corrigez les deux problèmes détectés, exécutez ruff check . et les tests, puis faites un commit et poussez-le.
  9. Ouvrez la nouvelle exécution. Dans l’étape de actions/setup-python, trouvez la ligne du cache. Dans l’étape finale après la tâche, trouvez Cache saved with the key (cache enregistré avec la clé).
Afficher le résultat attendu

La première exécution est rouge. L’étape Lint (analyse statique) affiche B904 dans ticket_api/config.py:28:13 et I001 dans ticket_api/routes.py:1:1, puis Found 2 errors. (2 erreurs trouvées). Votre ordinateur affiche les deux mêmes erreurs. Set up job indique Image: ubuntu-24.04 (image : ubuntu-24.04) et une version (20260927.320.1 le jour du test; la vôtre peut être plus récente). Il indique aussi une région Azure qui peut changer d’une exécution à l’autre. La deuxième exécution est verte, avec All checks passed! (toutes les vérifications ont réussi) dans Lint. Son étape Python indique pip cache is not found (cache pip introuvable), car l’exécution échouée n’a enregistré aucun cache. Ses dernières étapes enregistrent un cache d’environ 44 Mo. Une troisième exécution avec le même fichier de verrouillage indiquerait Cache restored successfully (cache restauré avec succès).

À vous de jouer : prévoir les résultats de l’analyseur statique

Section intitulée « À vous de jouer : prévoir les résultats de l’analyseur statique »

Faites ceci uniquement sur votre ordinateur. Ne poussez pas ces changements. Ajoutez une ligne au début de ticket_api/main.py : import os. Le module n’utilise jamais os. Faites une prédiction : combien de problèmes ruff check . signale-t-il, et avec quels codes de règle? Est-ce que --fix peut les corriger? Ensuite, exécutez la commande, puis retirez la ligne avec ruff check --fix ..

Afficher la réponse

Deux problèmes, pas un seul. La sortie (abrégée) :

I001 [*] Import block is un-sorted or un-formatted
--> ticket_api/main.py:1:1
...
F401 [*] `os` imported but unused
--> ticket_api/main.py:1:8
...
Found 2 errors.
[*] 2 fixable with the `--fix` option.

F401 désigne l’importation inutilisée. I001 désigne l’ordre : os doit venir après logging. Les deux portent [*], donc ruff check --fix . corrige les deux en retirant la ligne. L’ordinateur de test a affiché Found 1 error (1 fixed, 0 remaining). (1 erreur trouvée, 1 corrigée, 0 restante). Le retrait a aussi corrigé l’ordre. Un exécuteur signalerait les deux mêmes problèmes, avec les mêmes versions. Voilà pourquoi il faut exécuter ruff check . avant chaque poussée : la réponse de l’exécuteur n’est jamais une surprise.

Erreur courante

Le fichier de flux de travail ne démarre pas

Symptôme : vous poussez un flux de travail, et aucune tâche ne démarre. L’onglet Actions affiche plutôt une erreur pour le fichier de flux de travail. Cela arrive lorsque GitHub ne peut pas lire le YAML. (Cette erreur a été faite volontairement sur l’ordinateur de test, pas dans le dépôt de référence.)

Cause : une ligne a une mauvaise indentation. En YAML, les espaces au début d’une ligne déterminent à quel élément elle appartient. Ici, with: a été déplacé de deux espaces vers la gauche, hors de son étape :

.github/workflows/ci.yml (wrong)
- uses: actions/setup-python@v7
with:
python-version: "3.14.8"

Diagnostic : vérifiez le fichier sur votre ordinateur avec actionlint, un outil de vérification des fichiers de flux de travail. Il s’exécute dans un conteneur, donc il n’y a rien à installer :

Terminal, in the project folder
docker run --rm -v "$PWD":/repo -w /repo rhysd/actionlint:latest .github/workflows/*.yml

La sortie :

.github/workflows/ci.yml:25:6: could not parse as YAML: did not find expected '-' indicator [syntax-check]
|
25 | steps:
| ^~~~~

Le numéro de ligne pointe vers la liste qui ne fonctionne plus (steps:), pas vers la ligne incorrecte. Lisez les lignes en dessous.

Correction : replacez with: sous uses:, deux espaces à droite du -. Exécutez actionlint à nouveau : aucune sortie signifie qu’aucun problème n’a été trouvé. Ensuite, faites un commit et poussez-le.

Pour un vrai service d’IA : un flux de travail est du code qui protège la production. Vérifiez-le comme du code : actionlint avant la poussée, et une demande de tirage (pull request) pour chaque changement. Cela s’applique une fois que Publier les preuves ajoute l’ensemble de règles.

Vérifiez votre compréhension

La tâche 1 installe les paquets du fichier de verrouillage. Pourquoi la tâche 2 doit-elle récupérer le code à nouveau, et pourquoi ne peut-elle pas utiliser les paquets de la tâche 1?

Afficher la réponse

Chaque tâche s’exécute sur une nouvelle machine virtuelle (VM), et GitHub supprime la VM lorsque la tâche se termine. La tâche 2 démarre sur une autre VM propre, sans rien de la tâche 1 : aucun code, paquet ou fichier. Seul ce que la tâche 1 a enregistré hors de sa VM demeure : un cache, un artefact ou son résultat (réussite ou échec). C’est un avantage. Le résultat de la tâche 2 dépend uniquement de ce qu’elle fait. Personne n’a donc besoin de savoir ce qui se trouvait sur une machine auparavant.

Vérifiez votre compréhension

Sam veut un pipeline plus rapide. Il propose de mettre tout le dossier .venv en cache avec la clé fixe venv. Quel problème survient lorsque requirements-lock.txt change, et comment le cache du cours l'évite-t-il?

Afficher la réponse

Avec une clé fixe, l’exécution suivante restaure l’ancien .venv, avec les anciennes versions. Les vérifications réussissent ou échouent alors avec des paquets que le fichier de verrouillage et l’image n’ont plus. Le cache a changé le résultat, ce qu’un cache ne doit jamais faire. La clé du cache du cours provient de l’empreinte de requirements-lock.txt. Un fichier de verrouillage modifié ne correspond donc jamais à un ancien cache. Il ne stocke aussi que les téléchargements de pip : pip installe toujours exactement les versions du fichier de verrouillage.

Vérifiez votre compréhension

Exercice pratique : montrez vos deux premières exécutions. Quelle étape a échoué lors de la première, quels codes de règle a-t-elle signalés, et qu'a affiché ruff check . sur votre ordinateur?

Afficher la réponse

La première exécution a échoué à l’étape Lint, avec le code de sortie 1 : B904 dans ticket_api/config.py à la ligne 28 et I001 dans ticket_api/routes.py à la ligne 1, puis Found 2 errors. Sur votre ordinateur, ruff check . a affiché les deux mêmes problèmes. Le fichier de verrouillage fournissait ruff 0.16.10 aux deux machines, et pyproject.toml leur fournissait les mêmes règles. La deuxième exécution, après le commit de correction, est verte, et son étape Lint indique All checks passed!

Récapitulatif

  • Un exécuteur est la machine qui exécute une tâche. Les exécuteurs hébergés par GitHub démarrent chaque tâche sur une nouvelle VM. N’utilisez jamais un exécuteur autohébergé pour un dépôt public.
  • Une VM propre rend chaque résultat reproductible : une tâche dispose uniquement de ce que le flux de travail installe.
  • Un cache peut accélérer une tâche, jamais la modifier. Sa clé provient du fichier de verrouillage, et il stocke uniquement des téléchargements.
  • Fixez les actions à une version majeure, ou au SHA complet d’un commit pour le verrouillage le plus strict.
  • La première exécution réelle a échoué à l’analyse statique, avec les deux mêmes problèmes sur l’exécuteur et votre ordinateur. Vous les avez corrigés, et l’exécution est devenue verte.

Ensuite, la tâche reçoit les vérifications utiles (tests et vérification de sécurité). Une deuxième tâche construit l’image une seule fois et la teste.

Leçon suivante : Construire et tester