Aller au contenu

Workflows GitHub Actions : guide complet

Mise à jour :

Qu’est-ce qu’un workflow ?

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 :

QuestionSection YAMLExemple
QUAND s’exécuter ?on:À chaque push, sur les PR, à minuit…
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…

Anatomie d’un workflow

Regardons un workflow réaliste et décortiquons chaque partie :

# ══════════════════════════════════════════════════════════════════════════════
# MÉTADONNÉES
# ══════════════════════════════════════════════════════════════════════════════
name: CI # Nom affiché dans l'interface GitHub
run-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 build

La hiérarchie : Workflow → Jobs → Steps

C’est la structure la plus importante à comprendre. Si vous ne retenez qu’une chose de ce guide, c’est celle-ci.

L’analogie de l’entreprise

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

Les règles fondamentales

ConceptEnvironnementExécutionPartage de fichiers
JobsMachines différentesEn parallèle (par défaut)Via artifacts uniquement
StepsMême machineSéquentiellementOui (même système de fichiers)

Conséquences pratiques :

  • Un fichier créé dans le step 1 est disponible dans le step 2 du même job
  • Un fichier créé dans le job A n’existe pas dans le job B (machines différentes !)
  • Pour qu’un job attende un autre, il faut le déclarer avec needs:
  • Pour partager des fichiers entre jobs, il faut utiliser les artifacts

Pourquoi cette isolation ?

L’isolation entre jobs est une feature de sécurité et de fiabilité :

  • Parallélisation : les jobs peuvent tourner sur des machines différentes, simultanément
  • Reproductibilité : chaque job démarre avec un environnement propre
  • Isolation des échecs : un job planté ne corrompt pas les autres

L’erreur classique du débutant

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/ introuvable

Pourquoi ç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.

Les événements déclencheurs (on:)

La section on: définit quand votre workflow démarre. C’est le “déclencheur” qui réveille votre automatisation.

Les déclencheurs les plus courants

DéclencheurQuand ça se déclencheCas d’usage typique
pushCode poussé sur une brancheTests, build
pull_requestPR ouverte ou mise à jourValidation avant merge
workflow_dispatchBouton manuelDéploiement à la demande
scheduleHeure programmée (cron)Scans de sécurité nocturnes
releaseRelease publiéePublication de packages

Push et Pull Requests (les plus courants)

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]

Déclenchement manuel (très pratique pour les déploiements)

on:
workflow_dispatch:
inputs:
environment:
description: 'Où déployer ?'
required: true
type: choice
options:
- staging
- production

Vous verrez alors un bouton “Run workflow” dans l’interface GitHub avec un menu déroulant pour choisir l’environnement.

Planification (pour les tâches nocturnes)

on:
schedule:
# Tous les jours à 3h du matin (UTC)
- cron: '0 3 * * *'

Les jobs : vos unités de travail

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.

Ce qu’il faut savoir sur les jobs

  • Chaque job démarre sur une machine vierge (fresh VM)
  • Les jobs s’exécutent en parallèle par défaut
  • Un job peut dépendre d’autres jobs (avec needs:)
  • Un job peut avoir ses propres permissions (plus restrictives que le workflow)
  • Un job peut cibler un environment spécifique (staging, production)

Structure d’un job

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"

Les propriétés clés d’un job

PropriétéObligatoireDescription
runs-onOuiMachine virtuelle (ubuntu-24.04, windows-2022, macos-14)
stepsOuiListe des étapes à exécuter
nameNonNom affiché dans l’interface (recommandé)
needsNonJobs à attendre avant de démarrer
ifNonCondition d’exécution
timeout-minutesNonDurée max avant échec (défaut: 360 min !)
permissionsNonPermissions GITHUB_TOKEN pour ce job
environmentNonEnvironnement cible (staging, production…)
envNonVariables d’environnement

Dépendances entre jobs

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.sh

Visualisation de l’exécution :

test ──────────┐
├──→ deploy
build ─────────┘
│ (build attend test)

Les steps : vos actions unitaires

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…

Deux façons d’écrire un step

Il existe deux types de steps, et vous les utiliserez constamment :

TypeSyntaxeUsageExemple
Actionuses:Composant réutilisableCloner le repo, setup Node…
Commanderun:Script shellnpm 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 test

Propriétés utiles des steps

Chaque step peut avoir des propriétés qui contrôlent son comportement :

PropriétéDescriptionExemple
nameNom affiché dans les logs"Run unit tests"
ifCondition d’exécutionif: success(), if: failure()
continue-on-errorNe pas échouer le job si ce step échouetrue
timeout-minutesDurée max du step15
working-directoryDossier d’exécution./frontend
envVariables d’environnement pour ce stepDEBUG: 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 test

Les variables de contexte

GitHub met à disposition des variables de contexte qui contiennent des informations sur l’exécution en cours. Vous les utilisez avec la syntaxe ${{ contexte.variable }}.

Les contextes les plus utiles

ContexteCe qu’il contientExemples
githubInfos sur l’événementgithub.ref_name, github.actor, github.sha
secretsVos secretssecrets.DOCKER_TOKEN
varsVos variablesvars.NODE_VERSION
envVariables d’environnementenv.MY_VAR
matrixValeur courante de la matrixmatrix.os, matrix.node
needsOutputs des jobs précédentsneeds.build.outputs.version

Exemples concrets

# 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'environnement
env:
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 }}

Les 5 règles d’or des workflows

Ces règles vous éviteront 90% des problèmes :

1. Un workflow = une responsabilité

É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 versions

Pourquoi ? Un workflow qui échoue vous dit immédiatement est le problème. “Le déploiement a échoué” est plus utile que “le workflow CI a échoué”.

2. Toujours commencer par actions/checkout

C’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'abord
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- run: npm test

3. Permissions minimales

Dé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.

4. Nommer chaque step

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és
steps:
- name: Checkout repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Install dependencies
run: npm ci
- name: Run unit tests
run: npm test

Votre premier workflow complet

Pour 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 test

Les erreurs classiques (et comment les éviter)

Ces 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.

Erreur 1 : Oublier le checkout

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 travaille
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- run: npm test # Maintenant package.json existe

Comment 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).

Erreur 2 : Exposer un secret dans les logs

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 }}

Erreur 3 : Jobs parallèles non intentionnels

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.

Erreur 4 : Timeout par défaut trop long

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ème
jobs:
test:
runs-on: ubuntu-24.04
timeout-minutes: 15 # Échoue après 15 min max
steps:
- run: npm test

Recommandations de timeout :

Type de jobTimeout suggéré
Tests unitaires10-15 min
Build application15-20 min
Tests d’intégration20-30 min
Déploiement10-15 min

Récapitulatif

ErreurSymptômeSolution
Pas de checkout”file not found”Ajouter actions/checkout en premier
Secret exposéToken visible dans les logsUtiliser env: au lieu de echo
Jobs parallèlesDéploiement avant fin du buildAjouter needs: job_precedent
Pas de timeoutJob qui tourne des heuresAjouter timeout-minutes:

Valider vos workflows avant de pousser

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 :

OutilCe qu’il faitQuand l’utiliser
actionlintAnalyse statique (syntaxe, types, références)Avant chaque commit
ScorecardAudit sécurité (permissions, épinglage, patterns dangereux)Avant mise en production

actionlint : le linter indispensable

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é.

Terminal window
# Installation
brew install actionlint # macOS
# ou
go install github.com/rhysd/actionlint/cmd/actionlint@latest
# Validation
actionlint # Tous les workflows
actionlint .github/workflows/ci.yml # Un fichier spécifique

Exemple 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.

Scorecard : l’audit sécurité

OpenSSF Scorecard audite la posture sécurité de vos workflows. Trois checks sont particulièrement utiles :

CheckCe qu’il vérifie
Token-PermissionsPermissions write au niveau job (pas workflow)
Pinned-DependenciesActions épinglées par SHA
Dangerous-WorkflowPatterns dangereux (pull_request_target + checkout fork)
Terminal window
# Installation
brew install scorecard # macOS
# ou
go install github.com/ossf/scorecard/v5/cmd/scorecard@latest
# Audit des workflows
scorecard --local . --checks Token-Permissions,Pinned-Dependencies,Dangerous-Workflow
# Audit complet avec détails
scorecard --local . --show-details

Exemple 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 detected

Objectif : visez 8+ sur chaque check. Un score de 10/10 sur Token-Permissions et Dangerous-Workflow est atteignable facilement.

Workflow de validation recommandé

Avant chaque push, exécutez ce script :

validate-workflows.sh
#!/bin/bash
echo "🔍 Validation statique avec actionlint..."
actionlint
if [ $? -ne 0 ]; then
echo "❌ Erreurs de syntaxe détectées"
exit 1
fi
echo "� Audit sécurité avec Scorecard..."
scorecard --local . --checks Token-Permissions,Pinned-Dependencies,Dangerous-Workflow
if [ $? -ne 0 ]; then
echo "⚠️ Problèmes de sécurité détectés (voir ci-dessus)"
fi
echo "�🚀 Test d'exécution avec act..."
act -n
if [ $? -ne 0 ]; then
echo "❌ Erreurs d'exécution détectées"
exit 1
fi
echo "✅ Workflows valides !"

Passez à la pratique

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 :

  • Créer un workflow de A à Z
  • Configurer le déclenchement sur push
  • Rechercher sur la GitHub Marketplace les actions dont vous avez besoin (checkout, setup-python)
  • Ajouter les steps pour :
    • Cloner le code
    • Installer Python
    • Installer les dépendances
    • Lancer les tests
  • Valider votre travail localement avant de pousser (avec actionlint et act)

Duré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.

Accéder au TP 01

Contrôle des connaissances

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.

Pourquoi ce contrôle ?

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 ! 🚀

Liens utiles