Chapitre 1219 minDébutant
GitHub Actions : premier pipeline d'intégration continue
Par Josselyn C., prof de BTS SIO. Mis à jour le .
À la fin de ce chapitre, tu sauras
- expliquer l'intégration continue et le vocabulaire de GitHub Actions ;
- écrire un premier workflow qui lance les tests d'une API Node.js ;
- bloquer une fusion quand les tests échouent, et protéger les secrets ;
- lire un échec et tenir les dépendances à jour avec Dependabot.
3 exercices corrigés1 schéma
GitHub Actions est le service d'automatisation de GitHub : à chaque push ou pull request, il lance sur une machine de GitHub les étapes que tu as décrites dans un fichier YAML, par exemple installer les dépendances, vérifier le code et exécuter les tests. C'est l'outil le plus simple pour mettre en place l'intégration continue sur un projet de BTS. Ce chapitre explique le vocabulaire (workflow, job, step, runner), construit pas à pas un premier pipeline pour une API Node.js, puis montre comment l'utiliser pour bloquer la fusion d'une pull request qui casse les tests, gérer les secrets et lire un échec.
L'intégration continue en deux phrases
L'intégration continue (CI, continuous integration) consiste à intégrer souvent le travail de chacun dans la branche commune, et à vérifier automatiquement chaque intégration : compilation, analyse du code, tests. Le déploiement continu (CD) va plus loin : si toutes les vérifications passent sur main, l'application est livrée automatiquement.
Sans intégration continue, les tests ne tournent que si quelqu'un pense à les lancer, sur sa machine, avec sa configuration. Avec elle, chaque pull request affiche une coche verte ou une croix rouge avant même la relecture : « ça marchait chez moi » ne suffit plus.
Le référentiel du BTS SIO SLAM cite la compétence « Intégrer en continu les versions d'une solution applicative » : un pipeline qui lance tes tests à chaque push en est la preuve la plus directe.
Le vocabulaire de GitHub Actions
| Terme | Définition |
|---|---|
| Workflow | Un fichier YAML dans .github/workflows/ qui décrit une automatisation ; un dépôt peut en avoir plusieurs |
Événement (on) | Ce qui déclenche le workflow : push, pull_request, un horaire, un clic manuel |
| Job | Un ensemble d'étapes exécutées sur une même machine ; les jobs d'un workflow tournent en parallèle, sauf dépendance déclarée |
| Step | Une étape d'un job : une commande (run) ou une action réutilisable (uses) |
| Action | Un composant publié qu'on réutilise, comme actions/checkout qui récupère le code du dépôt |
| Runner | La machine virtuelle qui exécute un job : Ubuntu, Windows ou macOS, fournie par GitHub, ou une machine à toi (self-hosted runner) |
Un premier workflow pour une API Node.js
Crée le fichier .github/workflows/ci.yml à la racine du dépôt de Sorbier Frais :
name: CI
on:
push:
branches: [main]
pull_request:
jobs:
tests:
runs-on: ubuntu-latest
steps:
- name: Récupérer le code
uses: actions/checkout@v7
- name: Installer Node.js
uses: actions/setup-node@v7
with:
node-version: 24
cache: npm
- name: Installer les dépendances
run: npm ci
- name: Vérifier le style du code
run: npm run lint
- name: Lancer les tests
run: npm test
Ligne par ligne :
| Ligne | Rôle |
|---|---|
name: CI | Le nom affiché dans l'onglet Actions et sur les pull requests |
on: push: branches: [main] | Déclenche le workflow à chaque push sur main |
on: pull_request: | Et à chaque pull request, ouverte ou mise à jour, quelle que soit la branche |
jobs: tests: | Un job nommé tests |
runs-on: ubuntu-latest | Il tourne sur la dernière version d'Ubuntu proposée par GitHub |
uses: actions/checkout@v7 | Récupère le code du dépôt sur le runner ; sans elle, la machine est vide |
uses: actions/setup-node@v7 | Installe Node.js 24, une version LTS maintenue jusqu'en avril 2028 ; cache: npm garde les paquets téléchargés d'une exécution à l'autre |
run: npm ci | Installe exactement les versions du package-lock.json (plus strict et plus rapide que npm install) |
run: npm run lint et run: npm test | Lancent les scripts du package.json ; si la commande renvoie un code d'erreur, l'étape échoue et le job s'arrête |
Le @v7 désigne la version majeure de l'action. Les actions évoluent : quand GitHub annonce qu'une version est obsolète, change le numéro. Dependabot peut le faire pour toi (plus bas).
Commite et pousse :
git add .github/workflows/ci.yml
git commit -m "ci: lance le linter et les tests à chaque push"
git push
L'onglet Actions du dépôt affiche l'exécution en direct : chaque étape, sa durée, et sa sortie complète quand on la déplie.
Le même principe pour d'autres langages
Seules les étapes d'installation et de test changent :
| Projet | Étapes clés |
|---|---|
| PHP, Symfony | shivammathur/setup-php@v2 avec php-version: '8.5', puis composer install --no-progress et vendor/bin/phpunit |
| C#, .NET | actions/setup-dotnet@v6 avec dotnet-version: '10.0.x', puis dotnet restore, dotnet build --no-restore et dotnet test --no-build |
| Python | actions/setup-python@v7 avec python-version: '3.15', puis pip install -r requirements.txt et pytest |
| Java, Maven | actions/setup-java@v6 avec distribution: temurin et java-version: '25', puis mvn -B verify |
Pour une application de bureau WinForms, qui ne compile que sous Windows, remplace runs-on: ubuntu-latest par runs-on: windows-latest.
Tester avec une vraie base de données
Les tests d'intégration de l'API ont besoin d'une base MySQL. GitHub Actions peut démarrer un conteneur à côté du job, avec services :
jobs:
tests:
runs-on: ubuntu-latest
services:
mysql:
image: mysql:8.4
env:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: sorbier_test
ports:
- 3306:3306
options: >-
--health-cmd="mysqladmin ping -h 127.0.0.1 -proot"
--health-interval=5s --health-timeout=3s --health-retries=10
env:
DB_HOST: 127.0.0.1
DB_USER: root
DB_PASSWORD: root
DB_NAME: sorbier_test
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
with:
node-version: 24
cache: npm
- run: npm ci
- run: mysql -h 127.0.0.1 -uroot -proot sorbier_test < sql/schema.sql
- run: npm test
Le mot de passe root n'est pas un secret : cette base est créée vide pour les tests et détruite à la fin du job. Les options --health-* font attendre GitHub que MySQL soit prêt avant de lancer les étapes. Le script de création des tables vient du dépôt ; si tu as besoin d'une base d'exemple, celle du cours SQL convient.
Bloquer la fusion si les tests échouent
Un pipeline rouge que personne ne regarde ne sert à rien. Rends-le obligatoire : dans les règles de la branche main (protéger la branche main), active Require status checks to pass et sélectionne le job tests. Le bouton de fusion d'une pull request reste alors grisé tant que le job n'est pas vert.
Ajoute un badge en haut de ton README, qui affiche l'état du dernier pipeline :

La commission de l'E6 voit tout de suite que tes tests tournent automatiquement.
Les secrets dans GitHub Actions
Une étape de déploiement a besoin d'un mot de passe ou d'une clé : jamais dans le fichier YAML, qui est versionné et lisible par tous ceux qui voient le dépôt. Enregistre-les dans Settings, Secrets and variables, Actions, New repository secret, puis utilise-les ainsi :
- name: Déployer sur le serveur de recette
run: ./scripts/deploy.sh
env:
SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
GitHub masque la valeur dans les journaux (***). Les secrets ne sont pas transmis aux workflows déclenchés par la pull request d'un fork : un inconnu ne peut pas les lire en proposant une modification malveillante. Le jeton GITHUB_TOKEN, créé automatiquement pour chaque exécution, donne au workflow des droits sur le dépôt ; limite-les au strict nécessaire :
permissions:
contents: read
Lire un échec
Quand une étape échoue, GitHub t'envoie un courriel, la pull request affiche une croix rouge, et l'onglet Actions déplie l'étape en erreur. La méthode :
- Lis la fin de la sortie de l'étape rouge : le message d'erreur est presque toujours dans les dernières lignes. Pour des tests, c'est le nom du test qui échoue, la valeur attendue et la valeur obtenue.
- Reproduis en local avec la même commande (
npm cipuisnpm test). Beaucoup d'échecs viennent d'une différence avec ta machine : un fichier ignoré qui existe chez toi mais pas dans le dépôt, une variable d'environnement définie dans ton.envlocal, un fichier dont la casse diffère (Notes.jsetnotes.jssont un seul fichier sous Windows et macOS, deux sous Linux). - Corrige, commite, pousse : le workflow repart tout seul sur la pull request.
Le bouton Re-run jobs relance un job sans nouveau commit, utile quand l'échec venait d'un incident réseau.
Le coût
Pour un dépôt public, les runners standards de GitHub sont gratuits et illimités. Pour un dépôt privé, l'offre gratuite inclut un quota mensuel de minutes, largement suffisant pour un projet de BTS : un pipeline Node.js de lint et de tests dure une à deux minutes. Les runners Windows et macOS consomment le quota plus vite que Linux : reste sur ubuntu-latest quand le projet le permet.
Tenir les dépendances à jour avec Dependabot
Dependabot, intégré à GitHub, surveille les dépendances de ton projet. Il t'alerte quand l'une d'elles a une vulnérabilité connue, et peut ouvrir automatiquement une pull request de mise à jour, que ton pipeline teste aussitôt. Active les alertes dans Settings, Security, puis ajoute le fichier .github/dependabot.yml :
version: 2
updates:
- package-ecosystem: npm
directory: /
schedule:
interval: weekly
- package-ecosystem: github-actions
directory: /
schedule:
interval: monthly
Le second bloc met aussi à jour les versions des actions utilisées dans tes workflows.
Déployer automatiquement avec GitHub Actions
Le même mécanisme peut livrer l'application quand main est verte : publier un site statique sur GitHub Pages (dans Settings, Pages, source GitHub Actions, avec les actions officielles actions/upload-pages-artifact et actions/deploy-pages), construire et pousser une image Docker vers un registre, ou se connecter en SSH au serveur de recette pour y lancer le script de déploiement. Le déploiement ajoute des questions de sécurité et de retour arrière : commence par un pipeline de tests fiable, puis automatise le déploiement vers un environnement de recette avant la production. Le cours Docker montre comment empaqueter l'application, et le cours Tests comment écrire les tests que le pipeline lance. Le cours Intégration et déploiement continus (CI/CD) va plus loin : écrire un workflow complet, la matrice et le cache, GitLab CI, les environnements et les stratégies de déploiement.
Exercices
Exercice 1 sur 3 Lire un workflow Réussi À revoir
name: Qualité
on:
pull_request:
branches: [main]
jobs:
php:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: shivammathur/setup-php@v2
with:
php-version: '8.5'
- run: composer install --no-progress
- run: vendor/bin/phpunit
- Ce workflow tourne-t-il quand Lina pousse un commit directement sur
main? - Que se passe-t-il si
composer installéchoue ? - Que faut-il régler sur GitHub pour qu'une pull request ne puisse pas être fusionnée quand les tests échouent ?
- Pourquoi l'étape
actions/checkoutest-elle indispensable ?
Voir la réponseMasquer la réponse de l'exercice 1
- Non : il n'est déclenché que par les pull requests dont la branche cible est
main. - L'étape échoue, le job s'arrête,
phpunitne tourne pas, et la pull request affiche une croix rouge. - Une règle de protection (ou un ruleset) sur
mainavec Require status checks to pass, en sélectionnant le jobphp. - Le runner démarre vide : sans
checkout, le code du dépôt n'y est pas, etcomposer installne trouve pas decomposer.json.
Tu avais trouvé ?
Exercice 2 sur 3 Trouver les problèmes Réussi À revoir
Ce workflow pose trois problèmes, dont un qui le fait échouer à coup sûr. Trouve-les.
name: CI
on: [push]
jobs:
tests:
runs-on: ubuntu-latest
steps:
- uses: actions/setup-node@v7
with:
node-version: 24
- run: npm ci
- run: npm test
env:
DB_PASSWORD: Kq7!vT2pL9zR
Voir la réponseMasquer la réponse de l'exercice 2
- L'étape
uses: actions/checkout@v7manque :npm cine trouve pas depackage.jsonsur un runner vide. - Un mot de passe est écrit en clair dans un fichier versionné. Il faut le stocker dans les secrets du dépôt et écrire
DB_PASSWORD: ${{ secrets.DB_PASSWORD }}, puis changer ce mot de passe, désormais exposé dans l'historique. Si c'est le mot de passe d'une base de test créée par le workflow, un service MySQL jetable comme dans ce chapitre évite d'avoir un secret. - Le workflow ne tourne que sur
push: les pull requests ouvertes depuis un fork ne seront pas testées. Ajouterpull_requestdanson(par exempleon: [push, pull_request], oupushlimité àmainpluspull_requestpour éviter les doublons).
Tu avais trouvé ?
Exercice 3 sur 3 Écrire un workflow Réussi À revoir
Écris un workflow ci.yml pour une application ASP.NET Core en .NET 10, dont les tests sont dans la solution : il doit tourner à chaque pull request et à chaque push sur main, restaurer les dépendances, compiler en mode Release et lancer les tests.
Voir la réponseMasquer la réponse de l'exercice 3
name: CI
on:
push:
branches: [main]
pull_request:
permissions:
contents: read
jobs:
build-et-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/setup-dotnet@v6
with:
dotnet-version: '10.0.x'
- run: dotnet restore
- run: dotnet build --no-restore --configuration Release
- run: dotnet test --no-build --configuration Release
ASP.NET Core tourne sous Linux : ubuntu-latest convient. --no-restore et --no-build évitent de refaire les étapes précédentes ; dotnet test --no-build doit utiliser la même configuration que la compilation.
Tu avais trouvé ?