Anaïs Sparesotto
CI/CD · GitHub ActionsIntermédiaire≈ 2h · 7 chapitres

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-latest dans 95 % des cas)
.github/workflows/ci.yml — squelette minimal
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

Sur un dépôt public, les minutes Actions sont illimitées. Sur un dépôt privé, tu as 2 000 minutes/mois gratuites pour un compte Free. Au-delà, c'est facturé à la minute. Optimiser sa CI a donc un impact direct sur la facture.

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 UTC

Variables 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 -- --coverage

npm ci, pas npm install

En CI, utilise toujours 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 test

Ici, 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: 7

L'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 habituel
  • npm run typecheck : tsc --noEmit pour 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

Active Dependabot dans les paramètres du dépôt : il ouvre automatiquement des PR pour mettre à jour tes dépendances. Avec ta CI en place, tu valides en un clic si les tests passent.

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

GitHub masque les secrets dans les logs uniquement si la valeur exacte apparaît. Si tu fais 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.0

OIDC 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-3

Chapitre 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: true

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

needs: 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

Si plusieurs dépôts d'une organisation ont la même CI, factorise-la dans un workflow réutilisable avec 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

Si un déploiement casse en prod, va dans l'onglet Actions, ouvre le run précédent (qui marchait) et clique Re-run all jobs. Tu as ton rollback en un clic, sans toucher au code.

🛠️ 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

  1. Crée .github/workflows/ci.yml qui se déclenche sur push et pull_request vers main.
  2. Job test avec :
    • 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)
  3. Job audit parallèle exécutant npm audit --audit-level=high.
  4. Active concurrency pour annuler les runs obsolètes sur la même branche.
  5. Réduit les permissions à contents: read uniquement.
  6. Sur GitHub : protège main en exigeant que test passe 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. 1

    Quelle version de actions/checkout est recommandée en 2025 ?

  2. 2

    À quoi sert concurrency.cancel-in-progress: true ?

  3. 3

    Pourquoi préférer npm ci à npm install en CI ?

  4. 4

    Comment limiter les permissions du GITHUB_TOKEN ?

  5. 5

    Quelle clé permet de déclencher manuellement un workflow depuis l'interface GitHub ?

  6. 6

    Que fait strategy.matrix ?

  7. 7

    Quel mécanisme permet de s'authentifier vers AWS depuis GitHub Actions sans stocker de clé long terme ?

  8. 8

    Différence entre secrets et vars ?

  9. 9

    Pourquoi pinner une action tierce par SHA plutôt que par tag ?

  10. 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 →