TP 01 : Votre premier workflow
Automatisez l’exécution des tests d’une application Node.js. Vous appliquerez tout ce que vous venez d’apprendre : structure du workflow, checkout, permissions, bonnes pratiques.
Mise à jour :
Un workflow est un fichier texte qui décrit une suite d’actions automatisées. Quand un événement se produit (push, pull request, heure programmée), GitHub lit ce fichier et exécute les instructions qu’il contient.
Concrètement, un workflow répond à 4 questions :
| Question | Section YAML | Exemple |
|---|---|---|
| QUAND s’exécuter ? | on: | À chaque push, sur les PR, à minuit… |
| OÙ s’exécuter ? | runs-on: | Ubuntu, Windows, macOS |
| QUOI faire ? | steps: | Tester, builder, déployer… |
| DANS QUELLES CONDITIONS ? | if: | Seulement sur main, si les tests passent… |
Regardons un workflow réaliste et décortiquons chaque partie :
# ══════════════════════════════════════════════════════════════════════════════# MÉTADONNÉES# ══════════════════════════════════════════════════════════════════════════════name: CI # Nom affiché dans l'interface GitHubrun-name: Tests pour ${{ github.ref_name }} # Nom dynamique de l'exécution
# ══════════════════════════════════════════════════════════════════════════════# QUAND ? (Événements déclencheurs)# ══════════════════════════════════════════════════════════════════════════════on: push: branches: [main, develop] # Sur push vers ces branches pull_request: branches: [main] # Sur PR vers main workflow_dispatch: # Bouton manuel dans l'interface
# ══════════════════════════════════════════════════════════════════════════════# SÉCURITÉ (Permissions)# ══════════════════════════════════════════════════════════════════════════════permissions: contents: read # Lecture du code (minimum nécessaire)
# ══════════════════════════════════════════════════════════════════════════════# CONFIGURATION GLOBALE# ══════════════════════════════════════════════════════════════════════════════env: NODE_VERSION: '20' # Utilisable dans tous les jobs
# ══════════════════════════════════════════════════════════════════════════════# LES JOBS (Les unités de travail)# ══════════════════════════════════════════════════════════════════════════════jobs: # Job 1 : Tests test: runs-on: ubuntu-24.04 steps: # Actions épinglées par SHA (voir encart ci-dessous) - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version: ${{ env.NODE_VERSION }} - run: npm ci - run: npm test
# Job 2 : Build (attend que test soit OK) build: needs: test # Dépendance explicite runs-on: ubuntu-24.04 steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - run: npm ci - run: npm run buildC’est la structure la plus importante à comprendre. Si vous ne retenez qu’une chose de ce guide, c’est celle-ci.
Pensez à un workflow comme une entreprise :
Workflow (L'entreprise)│├── Job 1 "test" (Un département)│ ├── Step 1: Récupérer le code│ ├── Step 2: Installer Node│ ├── Step 3: Installer les dépendances│ └── Step 4: Lancer les tests│├── Job 2 "build" (Un autre département)│ ├── Step 1: Récupérer le code│ ├── Step 2: Installer les dépendances│ └── Step 3: Construire l'application│└── Job 3 "deploy" (Encore un autre) └── Step 1: Déployer sur le serveur| Concept | Environnement | Exécution | Partage de fichiers |
|---|---|---|---|
| Jobs | Machines différentes | En parallèle (par défaut) | Via artifacts uniquement |
| Steps | Même machine | Séquentiellement | Oui (même système de fichiers) |
Conséquences pratiques :
needs:L’isolation entre jobs est une feature de sécurité et de fiabilité :
Si vous ne comprenez pas cette distinction, vous ferez cette erreur :
# ❌ ERREUR : les fichiers buildés dans "build" ne sont PAS disponibles dans "deploy"jobs: build: runs-on: ubuntu-24.04 steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - run: npm run build # Crée un dossier dist/
deploy: needs: build # Attend que build soit terminé... runs-on: ubuntu-24.04 # ...mais c'est une AUTRE MACHINE ! steps: - run: ./deploy.sh dist/ # ❌ Erreur : dist/ introuvablePourquoi ça ne marche pas ? Le job deploy tourne sur une nouvelle machine
vierge. Le dossier dist/ créé par build est resté sur l’ancienne machine
(qui a été détruite).
La solution : utiliser les artifacts pour “télécharger” les fichiers d’un job à l’autre. Voir Artifacts vs Cache.
on:)La section on: définit quand votre workflow démarre. C’est le
“déclencheur” qui réveille votre automatisation.
| Déclencheur | Quand ça se déclenche | Cas d’usage typique |
|---|---|---|
push | Code poussé sur une branche | Tests, build |
pull_request | PR ouverte ou mise à jour | Validation avant merge |
workflow_dispatch | Bouton manuel | Déploiement à la demande |
schedule | Heure programmée (cron) | Scans de sécurité nocturnes |
release | Release publiée | Publication de packages |
on: # Quand quelqu'un pousse du code push: branches: - main # Branche exacte - 'release/*' # Wildcard : release/v1, release/v2... paths: - 'src/**' # Seulement si des fichiers dans src/ changent - '!**/*.md' # Mais pas les fichiers Markdown
# Quand une PR est ouverte ou mise à jour pull_request: branches: [main] types: [opened, synchronize, reopened]on: workflow_dispatch: inputs: environment: description: 'Où déployer ?' required: true type: choice options: - staging - productionVous verrez alors un bouton “Run workflow” dans l’interface GitHub avec un menu déroulant pour choisir l’environnement.
on: schedule: # Tous les jours à 3h du matin (UTC) - cron: '0 3 * * *'Un job est un ensemble de steps qui s’exécutent sur une même machine virtuelle. C’est l’unité de base de votre workflow.
needs:)jobs: mon-job: # Identifiant unique (lettres, chiffres, tirets, _) name: "Mon super job" # Nom affiché dans l'UI (optionnel mais recommandé) runs-on: ubuntu-24.04 # Machine d'exécution (obligatoire)
# Options de comportement timeout-minutes: 30 # Limite de temps (défaut: 6 heures !) continue-on-error: false # Arrêter le workflow si ce job échoue (défaut)
# Condition d'exécution if: github.event_name == 'push'
# Variables d'environnement pour ce job uniquement env: DEBUG: true
# Permissions spécifiques (plus restrictives que le workflow) permissions: contents: read
# Environnement cible (optionnel, pour les déploiements) environment: staging
# Les étapes à exécuter steps: - run: echo "Je suis une étape"| Propriété | Obligatoire | Description |
|---|---|---|
runs-on | Oui | Machine virtuelle (ubuntu-24.04, windows-2022, macos-14) |
steps | Oui | Liste des étapes à exécuter |
name | Non | Nom affiché dans l’interface (recommandé) |
needs | Non | Jobs à attendre avant de démarrer |
if | Non | Condition d’exécution |
timeout-minutes | Non | Durée max avant échec (défaut: 360 min !) |
permissions | Non | Permissions GITHUB_TOKEN pour ce job |
environment | Non | Environnement cible (staging, production…) |
env | Non | Variables d’environnement |
Par défaut, tous les jobs démarrent en même temps. Pour créer une séquence,
utilisez needs: :
jobs: test: runs-on: ubuntu-24.04 steps: - run: npm test
build: needs: test # Attend que "test" soit terminé et réussi runs-on: ubuntu-24.04 steps: - run: npm run build
deploy: needs: [test, build] # Attend PLUSIEURS jobs runs-on: ubuntu-24.04 steps: - run: ./deploy.shVisualisation de l’exécution :
test ──────────┐ ├──→ deploybuild ─────────┘ ↑ │ (build attend test)Les steps (étapes) sont les actions concrètes que vous voulez exécuter. Chaque step fait une chose : cloner le code, installer des dépendances, lancer des tests, déployer…
Il existe deux types de steps, et vous les utiliserez constamment :
| Type | Syntaxe | Usage | Exemple |
|---|---|---|---|
| Action | uses: | Composant réutilisable | Cloner le repo, setup Node… |
| Commande | run: | Script shell | npm install, pytest, make… |
steps: # ══════════════════════════════════════════════════════════════════════════════ # TYPE 1 : Action (uses) — composant réutilisable # ══════════════════════════════════════════════════════════════════════════════ - name: Récupérer le code uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: # Paramètres de l'action fetch-depth: 0
- name: Configurer Node.js uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version: '20' cache: 'npm' # Active le cache des dépendances
# ══════════════════════════════════════════════════════════════════════════════ # TYPE 2 : Commande shell (run) — script à exécuter # ══════════════════════════════════════════════════════════════════════════════ - name: Installer les dépendances run: npm ci
# Plusieurs commandes avec le pipe | - name: Build et test run: | echo "Building..." npm run build echo "Testing..." npm testChaque step peut avoir des propriétés qui contrôlent son comportement :
| Propriété | Description | Exemple |
|---|---|---|
name | Nom affiché dans les logs | "Run unit tests" |
if | Condition d’exécution | if: success(), if: failure() |
continue-on-error | Ne pas échouer le job si ce step échoue | true |
timeout-minutes | Durée max du step | 15 |
working-directory | Dossier d’exécution | ./frontend |
env | Variables d’environnement pour ce step | DEBUG: true |
steps: # Exécuter seulement si les steps précédents ont réussi (défaut) - name: Déployer if: success() run: ./deploy.sh
# Exécuter TOUJOURS (même si un step a échoué) - name: Nettoyer if: always() run: rm -rf temp/
# Exécuter seulement en cas d'échec - name: Notifier l'équipe if: failure() run: ./notify-slack.sh "Le build a échoué !"
# Continuer même si ce step échoue - name: Analyse optionnelle continue-on-error: true run: npm run analyze
# Timeout personnalisé (utile pour les tests lents) - name: Tests d'intégration timeout-minutes: 15 run: npm run test:integration
# Exécuter dans un dossier spécifique - name: Tests frontend working-directory: ./frontend run: npm testGitHub met à disposition des variables de contexte qui contiennent des
informations sur l’exécution en cours. Vous les utilisez avec la syntaxe
${{ contexte.variable }}.
| Contexte | Ce qu’il contient | Exemples |
|---|---|---|
github | Infos sur l’événement | github.ref_name, github.actor, github.sha |
secrets | Vos secrets | secrets.DOCKER_TOKEN |
vars | Vos variables | vars.NODE_VERSION |
env | Variables d’environnement | env.MY_VAR |
matrix | Valeur courante de la matrix | matrix.os, matrix.node |
needs | Outputs des jobs précédents | needs.build.outputs.version |
# Afficher des infos sur le contexte- name: Debug run: | echo "Branche: ${{ github.ref_name }}" echo "Auteur: ${{ github.actor }}" echo "SHA: ${{ github.sha }}"
# Utiliser un secret- name: Login Docker run: echo ${{ secrets.DOCKER_TOKEN }} | docker login -u user --password-stdin
# Condition basée sur le contexte- name: Deploy (main only) if: github.ref_name == 'main' run: ./deploy.sh
# Utiliser une variable d'environnementenv: NODE_VERSION: '20'
jobs: build: runs-on: ubuntu-24.04 steps: - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version: ${{ env.NODE_VERSION }}Ces règles vous éviteront 90% des problèmes :
Évitez les workflows géants qui font tout. Préférez plusieurs fichiers spécialisés :
.github/workflows/├── ci.yml # Tests et build├── security.yml # Scans de vulnérabilités├── deploy.yml # Déploiement└── release.yml # Publication de versionsPourquoi ? Un workflow qui échoue vous dit immédiatement où est le problème. “Le déploiement a échoué” est plus utile que “le workflow CI a échoué”.
actions/checkoutC’est l’erreur n°1 des débutants :
# ❌ Le code n'est PAS là par magie !steps: - run: npm test # Erreur : package.json introuvable
# ✅ Toujours checkout d'abordsteps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - run: npm testDéclarez toujours les permissions explicitement et au minimum nécessaire :
permissions: contents: read # Lecture seule par défaut
jobs: publish: permissions: contents: read packages: write # Écriture seulement pour le job qui publie steps: [...]Voir Sécurité GitHub Actions pour les détails.
Les logs sont votre outil de debug. Des noms explicites vous font gagner du temps :
# ❌ Difficile à lire dans les logs (et pas épinglé !)steps: - uses: actions/checkout@v4 - run: npm ci - run: npm test
# ✅ Logs clairs, compréhensibles et sécuriséssteps: - name: Checkout repository uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Install dependencies run: npm ci - name: Run unit tests run: npm testPour résumer, voici un exemple de workflow complet et sécurisé pour une application Node.js :
name: CI
on: push: branches: [main, develop] pull_request: branches: [main]
permissions: contents: read
jobs: test: runs-on: ubuntu-24.04 steps: - name: Checkout uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Setup Node.js uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version: '20' cache: 'npm'
- name: Install dependencies run: npm ci
- name: Run linter run: npm run lint
- name: Run tests run: npm testCes erreurs, tout le monde les fait au début. Comprendre pourquoi elles se produisent vous aidera à les éviter — et à les corriger rapidement quand elles arrivent.
Le symptôme : votre workflow échoue immédiatement avec “file not found”, “package.json not found” ou “command not found”.
Pourquoi ça arrive : un runner GitHub Actions démarre avec une machine
vide. Votre code n’est pas là par magie — il faut explicitement le
télécharger depuis le repository avec actions/checkout.
# ❌ Le runner est vide, le code n'existe pas !steps: - run: npm test # Erreur : package.json introuvable
# ✅ On récupère d'abord le code, puis on travaillesteps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - run: npm test # Maintenant package.json existeComment s’en souvenir : pensez à un coursier qui doit récupérer un colis avant de le livrer. Le checkout, c’est aller chercher le colis (votre code).
Le symptôme : votre clé API, token ou mot de passe apparaît en clair dans les logs du workflow — visible par tous ceux qui ont accès au repository.
Pourquoi ça arrive : GitHub masque automatiquement les secrets (***)
seulement quand ils sont utilisés via le contexte secrets. Si vous les
copiez dans une variable ou les affichez avec echo, le masquage ne fonctionne
plus.
# ❌ DANGER ! Le secret apparaît en clair dans les logs- run: echo "Token: ${{ secrets.API_KEY }}"
# ❌ DANGER ! Copier dans une variable puis echo = pas de masquage- run: | TOKEN=${{ secrets.API_KEY }} echo "Utilisation de $TOKEN" # Le token est visible !
# ✅ Passer via env : le masquage fonctionne- name: Deploy env: API_KEY: ${{ secrets.API_KEY }} run: ./deploy.sh # Le script lit $API_KEY, jamais affichéLa règle d’or : ne jamais echo un secret, même pour debug. Si vous
devez vérifier qu’un secret existe, testez sa longueur :
- run: | if [ -n "$API_KEY" ]; then echo "Secret présent (${#API_KEY} caractères)" fi env: API_KEY: ${{ secrets.API_KEY }}Le symptôme : votre job de déploiement démarre alors que le build n’est pas terminé, ou échoue car les fichiers produits par le job précédent n’existent pas.
Pourquoi ça arrive : par défaut, tous les jobs d’un workflow démarrent en
même temps (parallèlement). C’est voulu pour aller plus vite, mais il faut
explicitement déclarer les dépendances avec needs:.
# ❌ build et deploy démarrent EN MÊME TEMPS !jobs: build: runs-on: ubuntu-24.04 steps: - run: npm run build # Crée dist/
deploy: runs-on: ubuntu-24.04 steps: - run: ./deploy.sh dist/ # dist/ n'existe pas encore !
# ✅ deploy attend que build soit terminéjobs: build: runs-on: ubuntu-24.04 steps: - run: npm run build
deploy: needs: build # ← Attend la fin de build runs-on: ubuntu-24.04 steps: - run: ./deploy.sh dist/Attention : même avec needs:, les fichiers créés dans build ne sont
pas disponibles dans deploy (machines différentes). Utilisez les
artifacts pour
partager des fichiers entre jobs.
Le symptôme : un job bugué (boucle infinie, attente d’une ressource qui ne répond pas) tourne pendant des heures, consommant vos minutes GitHub Actions.
Pourquoi ça arrive : le timeout par défaut est de 6 heures (360 minutes). Un simple bug peut coûter cher en temps et en argent.
# ❌ Si npm test boucle, le job tourne 6 heures !jobs: test: runs-on: ubuntu-24.04 steps: - run: npm test
# ✅ Timeout explicite : échec rapide si problèmejobs: test: runs-on: ubuntu-24.04 timeout-minutes: 15 # Échoue après 15 min max steps: - run: npm testRecommandations de timeout :
| Type de job | Timeout suggéré |
|---|---|
| Tests unitaires | 10-15 min |
| Build application | 15-20 min |
| Tests d’intégration | 20-30 min |
| Déploiement | 10-15 min |
| Erreur | Symptôme | Solution |
|---|---|---|
| Pas de checkout | ”file not found” | Ajouter actions/checkout en premier |
| Secret exposé | Token visible dans les logs | Utiliser env: au lieu de echo |
| Jobs parallèles | Déploiement avant fin du build | Ajouter needs: job_precedent |
| Pas de timeout | Job qui tourne des heures | Ajouter timeout-minutes: |
Vous avez écrit un workflow, vous le poussez sur GitHub, et… erreur de syntaxe. Vous corrigez, re-poussez, nouvelle erreur. Ce cycle frustrant est évitable.
Deux outils complémentaires permettent de valider vos workflows localement :
| Outil | Ce qu’il fait | Quand l’utiliser |
|---|---|---|
| actionlint | Analyse statique (syntaxe, types, références) | Avant chaque commit |
| Scorecard | Audit sécurité (permissions, épinglage, patterns dangereux) | Avant mise en production |
actionlint détecte les erreurs sans exécuter le workflow : syntaxe YAML, propriétés mal orthographiées, actions inexistantes, expressions invalides, problèmes de sécurité.
# Installationbrew install actionlint # macOS# ougo install github.com/rhysd/actionlint/cmd/actionlint@latest
# Validationactionlint # Tous les workflowsactionlint .github/workflows/ci.yml # Un fichier spécifiqueExemple de sortie :
.github/workflows/ci.yml:15:9: property "node-verion" is not defined [expression].github/workflows/ci.yml:23:7: "actions/checkout@v4" is not pinned by SHA [security]L’erreur est trouvée en quelques millisecondes, pas après un push et une attente de plusieurs minutes.
OpenSSF Scorecard audite la posture sécurité de vos workflows. Trois checks sont particulièrement utiles :
| Check | Ce qu’il vérifie |
|---|---|
| Token-Permissions | Permissions write au niveau job (pas workflow) |
| Pinned-Dependencies | Actions épinglées par SHA |
| Dangerous-Workflow | Patterns dangereux (pull_request_target + checkout fork) |
# Installationbrew install scorecard # macOS# ougo install github.com/ossf/scorecard/v5/cmd/scorecard@latest
# Audit des workflowsscorecard --local . --checks Token-Permissions,Pinned-Dependencies,Dangerous-Workflow
# Audit complet avec détailsscorecard --local . --show-detailsExemple de sortie :
Token-Permissions: 10/10 ✅ permissions are declared at job level
Pinned-Dependencies: 6/10 ⚠️ actions/checkout@v4 is not pinned by SHA ✅ actions/setup-node@39370e... is pinned
Dangerous-Workflow: 10/10 ✅ no dangerous patterns detectedObjectif : visez 8+ sur chaque check. Un score de 10/10 sur Token-Permissions et Dangerous-Workflow est atteignable facilement.
Avant chaque push, exécutez ce script :
#!/bin/bashecho "🔍 Validation statique avec actionlint..."actionlintif [ $? -ne 0 ]; then echo "❌ Erreurs de syntaxe détectées" exit 1fi
echo "� Audit sécurité avec Scorecard..."scorecard --local . --checks Token-Permissions,Pinned-Dependencies,Dangerous-Workflowif [ $? -ne 0 ]; then echo "⚠️ Problèmes de sécurité détectés (voir ci-dessus)"fi
echo "�🚀 Test d'exécution avec act..."act -nif [ $? -ne 0 ]; then echo "❌ Erreurs d'exécution détectées" exit 1fi
echo "✅ Workflows valides !"Vous avez maintenant toutes les bases pour créer vos propres workflows. La théorie, c’est bien — mais rien ne vaut la pratique pour ancrer les concepts.
Le TP 01 : Votre premier workflow vous met dans une situation réaliste : vous rejoignez une équipe où les tests sont lancés manuellement (spoiler : ça ne marche jamais). Votre mission : automatiser tout ça.
Ce que vous allez faire :
pushDurée estimée : 30 minutes
TP 01 : Votre premier workflow
Automatisez l’exécution des tests d’une application Node.js. Vous appliquerez tout ce que vous venez d’apprendre : structure du workflow, checkout, permissions, bonnes pratiques.
Testez vos connaissances sur les workflows GitHub Actions. Ce quiz couvre les tout ce qui a été vu dans fondations (la sécurité, les secrets) et le contenu de ce guide.
Cet contrôle va vous permettre de valider vos connaissances sur le sujet abordé dans le guide. Il comporte des QCM, des questions vrai/faux et des réponses ouvertes à un mot.
🕒 Le chronomètre commence dès que vous cliquez sur Démarrer le test. Vous devrez terminer l’examen avant la fin du temps imparti.
🎯 Pour réussir, vous devez obtenir au moins 70% de bonnes réponses.
💡 Je ne fournis pas directement les réponses aux questions. Cependant, si certaines sont complexes, des pistes d’explication pourront être proposées dans le guide ou après l’examen.
Bonne chance ! 🚀