Anatomie d'un pipeline GitHub Actions

Première leçon du cours Intégration et déploiement continus, en accès libre : environ 15 minutes, 4 exercices et 4 questions de quiz. C'est la leçon de base du catalogue ; dans l'espace étudiant, elle est réécrite pour ton profil.

Objectifs

  • Expliquer ce que sont l'intégration continue et le déploiement continu
  • Lire et écrire un workflow : déclencheurs, jobs, étapes, runners
  • Enchaîner des jobs avec needs et protéger la branche principale par des vérifications obligatoires

Intégration continueWorkflow GitHub ActionsDéclencheursProtection de branche

L’intégration continue (CI) consiste à vérifier automatiquement chaque modification poussée sur le dépôt : installation, analyse statique, tests. Le déploiement continu (CD) prolonge la chaîne : une modification validée est livrée automatiquement, en préproduction puis en production. L’ensemble forme un pipeline. L’objectif n’est pas la vitesse pour elle-même, mais la confiance : un problème est détecté quelques minutes après son introduction, par une machine qui ne se lasse jamais de relancer les tests.

Dans ce cours, nous utilisons GitHub Actions, intégré à GitHub. Les notions (déclencheur, job, étape, artefact, environnement, secret) se retrouvent presque à l’identique dans GitLab CI, Azure Pipelines ou Jenkins.

Un premier workflow

Un workflow est un fichier YAML placé dans le dossier .github/workflows/ du dépôt. Le nom du fichier est libre (ci.yml, deploy.yml).

# .github/workflows/ci.yml
name: CI                         # nom affiché dans l'onglet Actions

on:                              # quand le workflow se déclenche
  push:
    branches: [main]
  pull_request:

jobs:
  test:                          # identifiant du job
    runs-on: ubuntu-latest       # machine virtuelle fournie par GitHub
    steps:
      - uses: actions/checkout@v4            # récupère le code du dépôt
      - uses: actions/setup-node@v4          # installe Node.js
        with:
          node-version: 22
      - run: npm ci                          # commande shell
      - run: npm test

Lisons-le de haut en bas :

  • on liste les déclencheurs : ici, chaque push sur main et chaque pull request.
  • jobs contient un ou plusieurs jobs. Chaque job démarre sur un runner neuf, une machine vierge : rien n’est conservé d’un job à l’autre, sauf ce que vous transmettez explicitement (artefacts, sorties).
  • steps est la liste ordonnée des étapes. Une étape fait soit uses: (une action réutilisable, publiée dans un dépôt, avec sa version après @), soit run: (une commande shell, exécutée par bash sur un runner Linux).
  • with: passe des paramètres à une action.

Si une étape échoue (code de sortie différent de 0), les étapes suivantes sont sautées et le job est marqué en échec. C’est exactement ce que l’on veut : npm test qui échoue rend le pipeline rouge.

Les déclencheurs

on:
  push:
    branches: [main]
    paths-ignore: ["docs/**", "**.md"]   # ne pas relancer pour la doc
  pull_request:
    branches: [main]                     # PR qui visent main
  workflow_dispatch:                     # bouton « Run workflow »
    inputs:
      environnement:
        type: choice
        options: [staging, production]
  schedule:
    - cron: "0 3 * * 1"                  # chaque lundi à 3 h UTC
  • push : à chaque push, filtrable par branches, tags et paths.
  • pull_request : à l’ouverture et à chaque nouveau commit d’une pull request. Le code testé est celui de la PR fusionné avec la branche cible.
  • workflow_dispatch : lancement manuel depuis l’onglet Actions, avec des paramètres (inputs) lus via ${{ inputs.environnement }}.
  • schedule : exécution planifiée, au format cron, en UTC.

Enchaîner des jobs avec needs

Par défaut, les jobs d’un workflow tournent en parallèle. Le mot-clé needs crée une dépendance :

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci && npm run lint
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci && npm test
  build:
    needs: [lint, test]          # attend que lint ET test réussissent
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci && npm run build

lint et test démarrent ensemble ; build ne démarre que si les deux sont verts. Si l’un échoue, build est marqué « skipped ». L’onglet Actions du dépôt affiche ce graphe, le statut de chaque job et le journal de chaque étape, que l’on déplie pour lire l’erreur.

Rendre la CI obligatoire

Un pipeline rouge n’empêche rien tant qu’on peut fusionner malgré lui. Dans Settings > Branches (règle de protection) ou Settings > Rules > Rulesets, on protège main :

  1. exiger une pull request avant de fusionner (plus de push direct) ;
  2. exiger que les status checks sélectionnés soient verts, par exemple les jobs lint et test ;
  3. éventuellement exiger que la branche soit à jour avec main.

Les noms proposés pour les checks sont ceux des jobs : choisissez des noms stables, car renommer un job requis bloque les pull requests jusqu’à la mise à jour de la règle.

Bonnes habitudes dès le départ

  • Gardez le pipeline rapide (quelques minutes) : sinon on cesse d’attendre son verdict.
  • Utilisez npm ci (ou pip install -r requirements.txt, composer install) : installation reproductible depuis le fichier de verrouillage.
  • Ajoutez timeout-minutes à un job susceptible de rester bloqué.
  • Vérifiez la syntaxe localement avec un linter de workflows comme actionlint avant de pousser.

Exercices

Les solutions et les tests restent sur le serveur. Dans l'espace étudiant, ton code est exécuté et corrigé, et le tuteur donne des indices avant la solution.

  1. TâcheDifficulté 11 indice

    Premier workflow vert

    Dans un dépôt GitHub contenant un projet Node.js avec un script test dans package.json, créez le fichier .github/workflows/ci.yml avec name: CI, les déclencheurs push sur main et pull_request, et un job nommé test sur ubuntu-latest qui exécute actions/checkout@v4, actions/setup-node@v4 (Node 22), npm ci puis npm test. Poussez sur main.

    • Le fichier se trouve exactement à `.github/workflows/ci.yml`
    • L'onglet Actions affiche une exécution du workflow « CI » déclenchée par le push
    • Le job `test` contient les étapes checkout, setup-node, `npm ci` et `npm test`
    • L'exécution se termine avec le statut vert (Success)
  2. TâcheDifficulté 21 indice

    Chaîner lint, test et build

    Modifiez ci.yml pour obtenir trois jobs : lint (npm run lint), test (npm test) et build (npm run build) avec needs: [lint, test]. Introduisez ensuite volontairement une erreur de lint dans une branche essai-lint, ouvrez une pull request vers main, observez, puis corrigez.

    • Sur main, le graphe de l'exécution montre `lint` et `test` en parallèle puis `build`
    • Sur la pull request avec l'erreur, `lint` est rouge et `build` est marqué « skipped »
    • Après correction et nouveau push sur la branche, les trois jobs sont verts
    • Aucune étape n'est dupliquée inutilement entre les jobs hormis checkout, setup-node et `npm ci`
  3. TâcheDifficulté 21 indice

    Rendre la CI obligatoire sur main

    Dans les paramètres du dépôt, créez une règle de protection (ou un ruleset) pour main qui exige une pull request et les status checks lint et test. Vérifiez qu’une pull request dont test échoue ne peut pas être fusionnée.

    • La règle cible la branche `main`
    • Les checks requis sont exactement `lint` et `test`
    • Sur une pull request avec un test cassé, le bouton de fusion est désactivé avec la mention que des checks requis ont échoué
    • Un push direct sur main est refusé (ou soumis à la pull request)
  4. QCMDifficulté 11 indice

    Ordre d'exécution des jobs

    Un workflow définit trois jobs a, b et c, sans aucun needs. Comment s’exécutent-ils ?

    • Dans l'ordre a, b, c
    • En parallèle, chacun sur son propre runner
    • Sur le même runner, en partageant les fichiers

Quiz de fin de leçon

  1. Où doit se trouver un fichier de workflow GitHub Actions ?

    • À la racine du dépôt
    • Dans .github/workflows/
    • Dans .actions/
  2. Quelle est la différence entre uses et run dans une étape ?

    • Aucune
    • uses appelle une action réutilisable, run exécute une commande shell
    • run appelle une action, uses une commande
  3. Quel déclencheur ajoute un bouton de lancement manuel ?

    • push
    • workflow_dispatch
    • schedule
  4. Si le job test échoue, que devient un job build qui a needs: test ?

    • Il s'exécute quand même
    • Il est sauté (skipped)
    • Il est relancé automatiquement

La suite du cours

  1. Tests et qualité dans la CI20 min
  2. Artefacts, sorties de jobs et releases15 min
  3. Déploiement automatique et environnements20 min
  4. Secrets et sécurité du pipeline20 min

Intégration et déploiement continus · Intermédiaire · 1 h 30