Tuto CI pour GitHub
Mettre en place une intégration continue sur GitHub Actions : workflows, jobs, cache, matrice, secrets, déploiement conditionnel. Tout ce qu'il faut pour sécuriser ton main.
À la fin du cours, tu sais
- Écrire un workflow GitHub Actions propre et lisible
- Mettre en place tests, lint, couverture en CI
- Gérer secrets et permissions avec le principe du moindre privilège
- Optimiser une CI lente (cache, parallélisme, concurrency)
- Aller du CI au CD avec environments et déploiement conditionnel
Prérequis
- Connaître Git et GitHub (pull requests, branches)
- Avoir un projet avec des tests automatisés
- Notions de YAML
Chapitre 1
Comprendre la CI et le modèle GitHub Actions
L'intégration continue automatise build, tests et qualité à chaque push, pour détecter les régressions tôt et garder main toujours déployable.
CI, CD, Continuous Deployment : ne pas confondre
- CI (Continuous Integration) : tests automatiques à chaque push, on s'assure que le code intègre proprement
- CD (Continuous Delivery) : on prépare des artefacts prêts à déployer, mais le déploiement reste manuel
- Continuous Deployment : on pousse automatiquement en prod dès que les tests passent
Anatomie d'un workflow GitHub Actions
- Workflow : un fichier YAML dans
.github/workflows/ - Event : ce qui déclenche le workflow (
push,pull_request,schedule...) - Job : un ensemble d'étapes qui tournent sur un runner
- Step : une action ou une commande shell
- Runner : la machine virtuelle qui exécute le job (
ubuntu-latestdans 95 % des cas)
name: CI
on:
push:
branches: [main]
pull_request:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: echo "Hello CI"Plan gratuit
Chapitre 2
Structurer ton premier workflow
Avant d'ajouter de la complexité, on cale les fondations : où placer le fichier, comment le déclencher, comment le rendre lisible.
Emplacement et nommage
Tous les workflows vivent dans .github/workflows/. Un fichier YAML par workflow. Nomme-les explicitement : ci.yml, deploy-staging.yml, release.yml.
Les déclencheurs courants
on:
push:
branches: [main] # uniquement sur main
pull_request:
paths: # uniquement si ces fichiers changent
- 'src/**'
- 'package.json'
workflow_dispatch: # bouton manuel dans l'UI GitHub
schedule:
- cron: '0 4 * * 1' # tous les lundis à 4h UTCVariables d'environnement
Tu peux définir des env à trois niveaux : workflow, job ou step. Les niveaux inférieurs surchargent les supérieurs.
env:
NODE_ENV: test
jobs:
build:
env:
LOG_LEVEL: debug
steps:
- run: echo "NODE_ENV=$NODE_ENV LOG_LEVEL=$LOG_LEVEL"Conditions et expressions
# Step qui ne tourne que sur main
- name: Deploy
if: github.ref == 'refs/heads/main'
run: ./deploy.sh
# Step qui ne tourne que si le précédent a réussi
- if: success()
run: echo "OK"Chapitre 3
Installer Node, cacher, matricer
Le combo gagnant pour une CI Node rapide : setup-node avec cache intégré, plus une matrice pour tester plusieurs versions en parallèle.
setup-node avec cache automatique
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc' # lit la version depuis .nvmrc
cache: 'npm' # cache automatique de ~/.npm
- run: npm ci
- run: npm run lint
- run: npm test -- --coveragenpm ci, pas npm install
npm ci : c'est plus rapide, ça installe exactement ce qui est dans package-lock.json, et ça échoue si le lock est désynchronisé. npm install peut subtilement modifier le lock et masquer des bugs.Matrice : tester sur plusieurs versions en parallèle
jobs:
test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
node: [20, 22]
os: [ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
cache: 'npm'
- run: npm ci
- run: npm testIci, 4 jobs tournent en parallèle (2 versions Node × 2 OS). fail-fast: false évite que tout s'arrête au premier échec, ce qui aide à débugger.
Chapitre 4
Qualité, sécurité et artefacts
Une CI utile ne fait pas que passer les tests : elle attrape les régressions de qualité et conserve les preuves.
Upload d'artefacts (couverture, rapports)
- run: npm test -- --coverage
- uses: actions/upload-artifact@v4
with:
name: coverage
path: coverage/
retention-days: 7L'artefact est téléchargeable depuis l'UI du run. Tu peux aussi le récupérer dans un autre job avec actions/download-artifact@v4.
Lint, typecheck, analyse statique
npm run lint: ESLint, Biome ou ton outil habituelnpm run typecheck:tsc --noEmitpour TypeScript- CodeQL : analyse de sécurité automatique, gratuite sur les repos publics, via
github/codeql-action@v3
Sécurité des dépendances
# Audit npm bloquant sur les vulnérabilités hautes
- run: npm audit --audit-level=high
# Review automatique des dépendances ajoutées dans une PR
- uses: actions/dependency-review-action@v4
if: github.event_name == 'pull_request'Dependabot
Chapitre 5
Secrets, permissions et sécurité
Une CI mal configurée est une porte ouverte. Quelques réflexes pour limiter les dégâts en cas de compromission.
Secrets : où, comment
- Repository secrets : disponibles dans tous les workflows du dépôt
- Environment secrets : scopés à un environnement (
production,staging), peuvent exiger une approbation - Organization secrets : partagés sur plusieurs dépôts d'une organisation
- run: ./deploy.sh
env:
API_TOKEN: ${{ secrets.API_TOKEN }}Ne jamais echo un secret
echo $API_TOKEN | base64, le résultat sera visible en clair. Réflexe : aucun secret ne doit transiter par stdout.Le principe du moindre privilège
Le token GITHUB_TOKEN a par défaut beaucoup de droits. Réduis-les explicitement au strict nécessaire :
permissions:
contents: read # lecture du code, c'est tout
pull-requests: write # commenter les PR
# tous les autres scopes par défaut à 'none'
jobs:
test:
runs-on: ubuntu-latest
# ...Pinner les actions tierces par SHA
Un tag (@v4) peut être déplacé par le mainteneur, voire compromis. Un SHA est immuable. Pour les actions tierces, pinne par SHA.
# Action officielle GitHub : tag OK
- uses: actions/checkout@v4
# Action tierce : pinne le SHA
- uses: super/sketchy-action@e2f4a3b1c9d8... # v2.1.0OIDC pour les clouds : pas de secret long terme
Pour t'authentifier à AWS, GCP ou Azure, n'utilise plus de clés stockées en secret. GitHub peut émettre un jeton OIDC court signé que ton cloud accepte. Plus de fuite possible.
permissions:
id-token: write # nécessaire pour OIDC
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123:role/github-deploy
aws-region: eu-west-3Chapitre 6
Performance et parallélisme
Une CI lente, on l'évite. Voilà comment passer de 12 minutes à 3 minutes sur un projet de taille moyenne.
Concurrency : annuler les runs obsolètes
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: trueSi tu pushes 3 commits coup sur coup, sans concurrency tu lances 3 builds. Avec cancel-in-progress: true, les anciens sont annulés dès qu'un nouveau démarre. Moins de minutes brûlées, feedback plus rapide.
Chaîner et paralléliser les jobs
jobs:
install:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 22, cache: 'npm' }
- run: npm ci
lint:
needs: install
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 22, cache: 'npm' }
- run: npm ci
- run: npm run lint
test:
needs: install
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 22, cache: 'npm' }
- run: npm ci
- run: npm testneeds: définit les dépendances. Ici, lint et test tournent en parallèle après install. Le cache npm évite de re-télécharger les dépendances dans chaque job.
Cache personnalisé
- uses: actions/cache@v4
with:
path: |
.next/cache
node_modules/.cache
key: ${{ runner.os }}-build-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-build-Workflows réutilisables
on: workflow_call. Les autres workflows l'appellent avec uses: org/repo/.github/workflows/ci.yml@main.Chapitre 7
Du CI au CD : déployer en confiance
Une fois les tests verts, le même workflow peut publier ou déployer. La clé : les environments GitHub pour cadrer ça proprement.
Environments avec règles de protection
Crée des environments dans Settings → Environments : staging, production. Tu peux exiger une approbation manuelle, limiter aux branches autorisées, et stocker des secrets dédiés.
jobs:
test:
runs-on: ubuntu-latest
steps: # ...
deploy:
needs: test
if: github.ref == 'refs/heads/main'
environment:
name: production
url: https://anais-formation-tech.fr
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: ./deploy.sh
env:
DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}Déploiement statique : Vercel, Netlify, Pages
Pour un site Next.js, Astro ou Vite, le plus simple est de connecter le dépôt à Vercel ou Netlify, qui s'occupent du build + déploiement à chaque push. Tu n'as alors plus qu'un job test en GitHub Actions, le déploiement est géré ailleurs.
Publier un package npm
on:
push:
tags: ['v*']
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
registry-url: 'https://registry.npmjs.org'
- run: npm ci
- run: npm test
- run: npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}Rollback simple
🛠️ Exercice optionnel
Mettre en place une CI complète sur un projet Node
Tu as un projet Node.js avec des tests existants (n'importe quel projet perso fera l'affaire). Tu vas construire une CI propre, performante et sécurisée.
Ta mission
- Crée
.github/workflows/ci.ymlqui se déclenche surpushetpull_requestversmain. - Job
testavec :- matrice Node 20 et 22
- cache npm via
actions/setup-node@v4 - lint, typecheck et tests avec couverture
- upload de la couverture en artefact (rétention 7 jours)
- Job
auditparallèle exécutantnpm audit --audit-level=high. - Active
concurrencypour annuler les runs obsolètes sur la même branche. - Réduit les
permissionsàcontents: readuniquement. - Sur GitHub : protège
mainen exigeant quetestpasse avant un merge.
Livrable : la PR contenant le workflow, une capture d'un run vert, et un badge de build dans le README.
Tu bloques ? Des indices, à dévoiler quand tu en as besoin.
Indice 1
Indice masqué.
Indice 2
Indice masqué.
Indice 3
Indice masqué.
✅ QCM de fin de cours
Teste tes acquis
10 questions, plusieurs réponses parfois possibles. Coche tout ce qui te semble juste, puis valide pour voir ton score et les explications.
- 1
Quelle version de
actions/checkoutest recommandée en 2025 ? - 2
À quoi sert
concurrency.cancel-in-progress: true? - 3
Pourquoi préférer
npm ciànpm installen CI ? - 4
Comment limiter les permissions du
GITHUB_TOKEN? - 5
Quelle clé permet de déclencher manuellement un workflow depuis l'interface GitHub ?
- 6
Que fait
strategy.matrix? - 7
Quel mécanisme permet de s'authentifier vers AWS depuis GitHub Actions sans stocker de clé long terme ?
- 8
Différence entre
secretsetvars? - 9
Pourquoi pinner une action tierce par SHA plutôt que par tag ?
- 10
Où sont stockés les artefacts uploadés par
actions/upload-artifact@v4?
Tu peux laisser des questions sans réponse, elles compteront comme fausses.
Tu veux ce cours pour ton équipe ?
Je peux adapter et animer ce cours pour tes formateur·ices ou tes apprenant·es, en présentiel ou en distanciel. Parlons-en pendant l'audit gratuit.
Réserver un audit gratuit →