1. Premier workflow
Sections
Réglages de lecture
Voix naturelle enregistrée pour ce cours, la même dans tous les navigateurs.
Aucune voix pour cette langue dans ce navigateur.
Plus de voix
Code et termes techniques en anglais
Désactivé : la voix de la page lit tout, sans pause entre deux voix.
Aucune voix pour cette langue dans ce navigateur.
Plus de voix
D’autres voix : Microsoft Edge propose des voix naturelles en ligne, et Windows (Paramètres › Heure et langue › Voix), macOS et Android en installent d’autres.
Le vocabulaire, mis en correspondance
Section intitulée « Le vocabulaire, mis en correspondance »Un workflow est un fichier YAML dans .github/workflows/. Un événement (un push, une pull request, un clic sur Run workflow) en démarre une exécution (run). Une exécution contient des jobs ; chaque job reçoit une machine virtuelle neuve, le runner, et exécute ses steps dans l’ordre. Un step lance soit un script shell (run:), soit une action (uses:), un morceau de code réutilisable publié dans un dépôt.
Le même vocabulaire en image : un événement démarre une exécution, et chaque job de l’exécution reçoit son propre runner.
flowchart LR event["événement : un push, une pull request, Run workflow"] run["exécution du workflow"] subgraph r1["runner : une machine virtuelle neuve"] job1["job"] --> s1["step run : un script shell"] --> s2["step uses : une action"] end subgraph r2["runner : une autre machine virtuelle neuve"] job2["job"] --> s3["ses propres steps, dans l'ordre"] end event --> run run --> job1 run --> job2| GitHub Actions | Azure Pipelines | Jenkins (déclaratif) |
|---|---|---|
workflow (.github/workflows/*.yml) |
pipeline (azure-pipelines.yml) |
Jenkinsfile |
événement (on:) |
trigger:, pr:, schedules: |
triggers { } |
| job | job (les stages sont facultatifs) | stage |
step : run: |
script: / pwsh: |
sh / bat |
step : uses: (action) |
tâche (DotNetCoreCLI@2) |
step de plugin |
runner (runs-on: ubuntu-latest) |
agent (pool: vmImage: ubuntu-latest) |
agent { label '…' } |
Deux conséquences de « chaque job reçoit une machine neuve » surprennent ceux qui viennent de Jenkins :
- rien n’est sur le disque au début d’un job, pas même ton code : tu le récupères explicitement (checkout) ;
- les fichiers ne passent pas d’un job au suivant : les jobs partagent des données par des outputs (leçon 4) ou des artefacts (leçon 5).
Le plus petit workflow utile
Section intitulée « Le plus petit workflow utile ».github/workflows/gha-01-hello.yml :
# GitHub Actions course, lesson 1: the smallest useful workflowname: "GHA 01: hello"
on: workflow_dispatch: push: branches: [main] paths: ['.github/workflows/gha-01-hello.yml']
jobs: hello: runs-on: ubuntu-latest steps: - name: Say hello run: echo "Hello from GitHub Actions"
- name: Where am I? run: | echo "Event: ${{ github.event_name }}" echo "Commit: ${{ github.sha }}" echo "Runner: $RUNNER_OS $RUNNER_ARCH" echo "Workdir: $(pwd)" ls -A
- name: Check out the repository uses: actions/checkout@v7
- name: Look again run: ls -A| Ligne | Signification |
|---|---|
name: |
le nom affiché dans l’onglet Actions |
on: workflow_dispatch |
ajoute un bouton Run workflow (et gh workflow run) |
on: push avec branches et paths |
s’exécute lors d’un push sur main qui modifie ce fichier |
jobs.hello.runs-on |
l’image du runner : Ubuntu hébergé par GitHub |
run: | |
un script sur plusieurs lignes, exécuté par bash sous Linux et macOS |
uses: actions/checkout@v7 |
l’action checkout, version majeure 7 |
L’exécution, étape par étape
Section intitulée « L’exécution, étape par étape »Le push du fichier a démarré une exécution. Avec GitHub CLI :
gh run list --workflow gha-01-hello.ymlgh run view 34845175861gh run view 34845175861 --log✓ main GHA 01: hello · 34845175861Triggered via push about 6 minutes ago
JOBS✓ hello in 4s (ID 103979246921)Le log du step Where am I? (horodatages retirés) :
##[group]Run echo "Event: push"echo "Event: push"echo "Commit: 61e7e4235b7bc138f2f35cd7f61195bdaed4b019"echo "Runner: $RUNNER_OS $RUNNER_ARCH"echo "Workdir: $(pwd)"ls -Ashell: /usr/bin/bash -e {0}Event: pushCommit: 61e7e4235b7bc138f2f35cd7f61195bdaed4b019Runner: Linux X64Workdir: /home/runner/work/learn/learnTrois choses à y lire :
-
${{ github.event_name }}a été remplacé avant l’exécution de bash. Le script affiché par le runner contient déjàpush: les expressions sont une substitution de texte faite par GitHub.$RUNNER_OSest différent : c’est une variable d’environnement, développée par bash. La leçon 4 y revient, parce que c’est important pour la sécurité. -
ls -An’a rien affiché. Le répertoire de travail/home/runner/work/learn/learnexiste mais il est vide. Aprèsactions/checkout, la même commande liste le dépôt :.git.gitattributes.github.gitignore.vscodeAGENTS.mdCLAUDE.mdREADME.md -
shell: /usr/bin/bash -e {0}: chaquerun:est écrit dans un script temporaire et exécuté avecbash -e, donc le step échoue à la première commande en échec.
Le step Set up job te dit ce que tu as obtenu :
Current runner version: '2.337.0'Ubuntu24.04.5LTSImage: ubuntu-24.04Version: 20260907.300.1GITHUB_TOKEN PermissionsContents: readMetadata: readPackages: readubuntu-latest est une étiquette mouvante : en septembre 2026, c’est Ubuntu 24.04. Et le job a reçu un GITHUB_TOKEN qui ne peut que lire le dépôt — la leçon 7 explique comment l’élargir ou le restreindre.
Piège : un workflow invalide échoue à chaque push
Section intitulée « Piège : un workflow invalide échoue à chaque push »Ma première version du workflow de la leçon 3 contenait une erreur YAML. La liste des exécutions affichait le chemin du fichier au lieu du nom du workflow, un échec en 0 seconde, et aucun job :
X main .github/workflows/gha-03-triggers.yml · 34845174629Triggered via push less than a minute ago
X This run likely failed because of a workflow file issue.La page web de l’exécution donne la raison : You have an error in your yaml syntax on line 41. Pire, le push suivant — qui ne touchait pas ce fichier, et le workflow a un filtre paths — a produit une autre exécution en échec : GitHub ne peut pas lire les filtres d’un fichier qu’il n’arrive pas à analyser. Le coupable :
run: echo "cron: ${{ github.event.schedule }}"En YAML, une valeur sans guillemets ne peut pas contenir : (deux-points suivi d’une espace). Une vérification locale l’attrape avant le push :
python -c "import yaml; yaml.safe_load(open('.github/workflows/gha-03-triggers.yml'))"yaml.scanner.ScannerError: mapping values are not allowed here in ".github/workflows/gha-03-triggers.yml", line 41, column 24La correction : un scalaire bloc (run: | et la commande sur la ligne suivante), ou mettre toute la valeur entre guillemets.
À retenir
Section intitulée « À retenir »- Workflow → exécution → jobs (un runner neuf chacun) → steps (
run:ouuses:). - Un job démarre avec un espace de travail vide :
actions/checkoutest presque toujours le premier step. ${{ }}est remplacé par GitHub avant l’exécution du script ;$VARest développé par le shell.gh run list,gh run viewetgh run view --loglisent les exécutions sans quitter le terminal.- Un workflow qui ne s’analyse pas apparaît, à chaque push, comme un échec en 0 seconde portant le nom de son fichier.
Exercices
Section intitulée « Exercices »- Dans
gha-01-hello.yml, qu’afficheraitls -Adans le stepWhere am I?siactions/checkoutétait le premier step ?
Solution
Les fichiers du dépôt (.git, .github, AGENTS.md…), comme le step Look again. Le checkout clone dans le répertoire de travail, /home/runner/work/learn/learn, là où démarre chaque run:.
- Ajoute un step qui échoue, lance le workflow avec
gh workflow run gha-01-hello.yml, et retrouve le step en échec depuis le terminal.
Solution
- name: Fail on purpose run: exit 1gh workflow run gha-01-hello.ymlgh run list --workflow gha-01-hello.yml --limit 1gh run view <run-id> --log-failed--log-failed n’affiche que les logs des steps en échec, qui se terminent par ##[error]Process completed with exit code 1.. Les steps suivants ne s’exécutent pas, sauf s’ils ont une condition comme if: always() (leçon 4).
- Pourquoi
echo "Runner: $RUNNER_OS"fonctionne-t-il dans un steprun:alors queecho "cron: $X"a cassé le YAML ?
Solution
La première ligne est dans un scalaire bloc (run: |), où YAML prend le texte tel quel. La seconde était un scalaire simple (sans guillemets) sur la même ligne que run:, et un scalaire simple ne peut pas contenir : . Le shell n’y est pour rien : le fichier est rejeté avant le démarrage de tout runner.