Aller au contenu
Réussir mon BTS SIO

Tape une notion, une commande ou le nom d'une épreuve.

Par exemple

Chapitre 335 minIntermédiaire

GitHub Actions : écrire un workflow YAML

Par Josselyn C., prof de BTS SIO. Mis à jour le .

À la fin de ce chapitre, tu sauras

  • écrire un workflow YAML valide dans .github/workflows ;
  • choisir les bons déclencheurs, filtres et horaires cron ;
  • enchaîner des jobs avec needs et des conditions if ;
  • lire les contextes et transmettre des valeurs entre étapes et jobs ;
  • éviter l'injection de script et limiter les droits du jeton.

5 exercices corrigés1 schéma

GitHub Actions est le service d'automatisation intégré à GitHub : un workflow, décrit dans un fichier YAML du dossier .github/workflows/, se déclenche sur un événement (un push, une pull request, un horaire, un clic) et exécute des jobs composés d'étapes sur des machines fournies par GitHub. Ce chapitre explique la syntaxe YAML, puis chaque partie d'un workflow : déclencheurs, jobs, étapes, expressions, variables et permissions. Il se termine par le premier workflow complet de sorbier-frais.

Où vivent les workflows

Un workflow est un fichier .yml ou .yaml placé dans .github/workflows/, à la racine du dépôt. Le nom du fichier est libre ; un dépôt peut en avoir autant qu'il veut, chacun avec ses déclencheurs.

text
sorbier-frais/
├── .github/
│   ├── workflows/
│   │   ├── ci.yml            ← lint et tests à chaque pull request
│   │   ├── deploy.yml        ← déploiement après fusion sur main
│   │   └── release.yml       ← publication d'une version sur un tag
│   └── dependabot.yml
├── src/
├── tests/
├── package.json
└── package-lock.json

GitHub lit les workflows dans le commit concerné : un workflow ajouté sur une branche s'applique aux push de cette branche. Deux exceptions : les déclencheurs schedule (horaire) et workflow_dispatch (lancement manuel) ne fonctionnent qu'une fois le fichier présent sur la branche par défaut.

Le YAML en dix minutes

YAML est un format texte pour décrire des données. Il n'a que trois constructions : des dictionnaires (des paires clé: valeur), des listes (des éléments précédés de - ) et des valeurs simples (texte, nombre, booléen).

yaml
# Un commentaire commence par un dièse
name: CI                     # une clé et sa valeur texte
on:                          # une clé dont la valeur est un dictionnaire…
  push:                      # …indenté de deux espaces
    branches: [main]         # une liste écrite sur une ligne
jobs:
  tests:
    runs-on: ubuntu-latest
    steps:                   # une liste écrite sur plusieurs lignes
      - run: npm ci
      - run: npm test

Les règles qui causent presque toutes les erreurs :

RègleCorrectFaux
L'indentation se fait avec des espaces, jamais des tabulationsdeux espaces par niveauune tabulation : found character '\t' that cannot start any token
Un espace après les deux-pointsruns-on: ubuntu-latestruns-on:ubuntu-latest (lu comme un texte, pas une clé)
Un élément de liste commence par un tiret et un espace- run: npm ci-run: npm ci
Les éléments d'une même liste ont la même indentationtous les - alignésun - décalé d'un espace : il change de parent
Les versions avec un zéro final se mettent entre guillemetspython-version: '3.10'python-version: 3.10, lu comme le nombre 3.1
Un texte qui commence par *, {, [, & ou !, ou qui contient : , se met entre guillemetscron: '0 6 * * 1'cron: 0 6 * * 1 est valide, mais - *.js ne l'est pas

Pour une commande sur plusieurs lignes, le bloc littéral | garde les retours à la ligne :

yaml
      - name: Préparer la base de test
        run: |
          mysql -h 127.0.0.1 -uroot -proot -e "CREATE DATABASE IF NOT EXISTS frais_test"
          mysql -h 127.0.0.1 -uroot -proot frais_test < sql/schema.sql

Les listes et les dictionnaires ont aussi une forme compacte : branches: [main, develop] vaut une liste de deux éléments, with: { node-version: 24 } un dictionnaire d'une clé.

L'anatomie d'un workflow

Voici un workflow annoté. Chaque partie est détaillée dans la suite du chapitre.

yaml
name: CI                                  # nom affiché dans l'onglet Actions

on:                                       # 1. les déclencheurs
  push:
    branches: [main]
  pull_request:

permissions:                              # 2. les droits du jeton GITHUB_TOKEN
  contents: read

concurrency:                              # 3. une seule exécution à la fois par branche
  group: ci-${{ github.ref }}
  cancel-in-progress: true

env:                                      # 4. des variables pour tous les jobs
  NODE_VERSION: 24

jobs:                                     # 5. les jobs, en parallèle par défaut
  lint:
    runs-on: ubuntu-latest                # la machine
    timeout-minutes: 10                   # durée maximale du job
    steps:                                # 6. les étapes, dans l'ordre
      - uses: actions/checkout@v7         # une action réutilisable
      - uses: actions/setup-node@v7
        with:                             # ses paramètres
          node-version: ${{ env.NODE_VERSION }}
          cache: npm
      - run: npm ci                       # une commande
      - run: npm run lint

  tests:
    runs-on: ubuntu-latest
    timeout-minutes: 15
    steps:
      - uses: actions/checkout@v7
      - uses: actions/setup-node@v7
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: npm
      - run: npm ci
      - run: npm test
push sur mainoupull requestWorkflow .github/workflows/ci.ymlJob lintrunner Ubuntu neufactions/checkoutactions/setup-nodenpm cinpm run lintJob testsrunner Ubuntu neufactions/checkoutactions/setup-nodenpm cinpm testLes étapes d’un job : en ordre, sur la même machine.Deux jobs : en parallèle, sans fichiers partagés.
Un événement déclenche le workflow ; ses jobs tournent en parallèle sur des machines distinctes ; les étapes d'un job s'enchaînent sur la même machine
TermeDéfinition
WorkflowUn fichier YAML de .github/workflows/ ; une exécution (run) est un lancement de ce workflow
ÉvénementCe qui déclenche le workflow : push, pull_request, schedule, workflow_dispatch…
JobUn ensemble d'étapes exécutées sur une même machine ; les jobs d'un workflow tournent en parallèle, sauf si needs les enchaîne
Étape (step)Une commande shell (run) ou une action (uses) ; les étapes d'un job s'exécutent dans l'ordre et partagent le même disque
ActionUn composant réutilisable publié dans un dépôt, comme actions/checkout
RunnerLa machine qui exécute un job : une machine virtuelle neuve, détruite à la fin

Les déclencheurs : on

ÉvénementIl se produit quandUsage typique
pushDes commits ou un tag sont poussésVérifier main, publier sur un tag
pull_requestUne pull request est ouverte, mise à jour ou rouverteLa CI avant fusion
workflow_dispatchQuelqu'un clique sur Run workflow (ou lance gh workflow run)Un déploiement manuel, une tâche ponctuelle
scheduleUne heure donnée, au format cronUn audit des dépendances chaque nuit
releaseUne release GitHub est publiéeConstruire et joindre les binaires
workflow_callUn autre workflow l'appelleUn workflow réutilisable (chapitre 4)
pull_request_targetComme pull_request, mais exécuté avec les droits du dépôt cibleCas très particuliers, dangereux (chapitre 7)

Filtrer les branches, les tags et les chemins. Les filtres limitent les exécutions inutiles :

yaml
on:
  push:
    branches: [main, 'release/**']   # main et toutes les branches sous release/
    tags: ['v*']                     # les tags qui commencent par v : v1.0.0, v2.3.1
    paths-ignore:                    # ne rien faire si seuls ces fichiers changent
      - 'docs/**'
      - '**.md'
  pull_request:
    branches: [main]                 # pour pull_request : la branche CIBLE de la fusion

* remplace n'importe quels caractères sauf /, ** traverse aussi les /. Pour pull_request, branches désigne la branche cible (celle dans laquelle on veut fusionner), pas la branche de travail. Quand un événement a à la fois un filtre branches et un filtre paths, les deux doivent correspondre.

Lancer à la main, avec des paramètres.

yaml
on:
  workflow_dispatch:
    inputs:
      environnement:
        description: Environnement cible
        type: choice
        options: [recette, production]
        default: recette
      dry-run:
        description: Simuler sans déployer
        type: boolean
        default: false

Les valeurs se lisent avec ${{ inputs.environnement }} et ${{ inputs.dry-run }}.

Planifier avec cron. La syntaxe a cinq champs : minute, heure, jour du mois, mois, jour de la semaine (0 ou 7 = dimanche).

yaml
on:
  schedule:
    - cron: '30 5 * * 1-5'   # à 5 h 30 UTC, du lundi au vendredi

Par défaut, l'heure est en UTC : 5 h 30 UTC, c'est 6 h 30 à Paris en hiver (UTC+1) et 7 h 30 en été (UTC+2). Depuis mars 2026, une clé timezone, placée à côté de cron, indique un fuseau horaire sous son nom officiel (Europe/Paris) : le workflow part alors à l'heure locale indiquée, toute l'année, changement d'heure compris.

yaml
on:
  schedule:
    - cron: '30 6 * * 1-5'   # à 6 h 30, heure de Paris, du lundi au vendredi
      timezone: Europe/Paris

L'intervalle minimal est de cinq minutes, l'exécution peut être retardée aux heures chargées, et sur un dépôt public sans activité pendant 60 jours, GitHub désactive les workflows planifiés.

Jobs, needs et conditions

Un job se déclare sous jobs, avec un identifiant (lint, tests, build) qui ne contient que des lettres, des chiffres, - et _.

CléRôleExemple
runs-onLa machineubuntu-latest, windows-latest, macos-latest
needsLes jobs qui doivent réussir avantneeds: [lint, tests]
ifUne condition pour lancer le jobif: github.ref == 'refs/heads/main'
timeout-minutesDurée maximale, 360 minutes par défauttimeout-minutes: 15
envDes variables pour toutes les étapes du jobenv: { NODE_ENV: test }
servicesDes conteneurs annexes, comme une base de donnéesvoir le chapitre 6
strategyUne matrice de variantesvoir le chapitre 4
environmentL'environnement de déploiementenvironment: production (chapitre 10)
outputsDes valeurs transmises aux jobs suivantsvoir plus bas

Sans needs, tous les jobs démarrent en même temps. Avec needs, ils forment un graphe : un job attend que tous ceux qu'il cite aient réussi. Si l'un échoue, les jobs qui en dépendent sont marqués skipped (ignorés).

yaml
jobs:
  lint:
    runs-on: ubuntu-latest
    steps: [...]
  tests:
    runs-on: ubuntu-latest
    steps: [...]
  build:
    needs: [lint, tests]                  # attend les deux
    runs-on: ubuntu-latest
    steps: [...]
  deploy:
    needs: build
    if: github.event_name == 'push' && github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    steps: [...]

Dans cet extrait, [...] remplace les étapes pour la lisibilité : ce n'est pas un workflow valide tel quel. lint et tests démarrent ensemble ; build attend les deux ; deploy ne tourne qu'après un push sur main, jamais pour une pull request.

Les fonctions de statut. Par défaut, une étape ou un job ne s'exécute que si tout ce qui précède a réussi : c'est la condition implicite success(). Trois autres fonctions changent ce comportement :

ConditionL'étape s'exécute…Usage
if: success()si tout a réussi (par défaut)le cas normal
if: failure()si une étape précédente a échouéenvoyer une alerte, publier les journaux d'erreur
if: always()dans tous les cas, même après une annulationpublier un rapport de tests, nettoyer
if: cancelled()si l'exécution a été annuléerare

continue-on-error: true sur une étape ou un job laisse le reste continuer même en cas d'échec. Réserve-le aux vérifications non bloquantes assumées (un test sur une version de Node.js expérimentale), jamais aux tests principaux.

Les étapes : run ou uses

Une étape fait soit une commande (run), soit un appel d'action (uses).

yaml
    steps:
      - name: Récupérer le code              # nom affiché dans le journal
        uses: actions/checkout@v7

      - name: Installer Node.js
        uses: actions/setup-node@v7
        with:                                # paramètres de l'action
          node-version: 24
          cache: npm

      - name: Lancer les tests
        id: tests                            # identifiant, pour lire ses sorties
        run: npm test -- --ci
        env:                                 # variables de cette étape
          NODE_ENV: test
        working-directory: ./api             # dossier d'exécution

Les commandes run. Sous Linux et macOS, une étape run s'exécute avec bash -e : elle s'arrête à la première commande qui échoue. Avec shell: bash écrit explicitement, GitHub ajoute -o pipefail : une commande en échec au milieu d'un tube (npm test | tee resultat.txt) fait aussi échouer l'étape. Sous Windows, le shell par défaut est PowerShell (pwsh).

Les actions uses. La référence a la forme propriétaire/dépôt@version :

RéférenceCe qu'elle désigneSûreté
actions/checkout@v7La dernière version 7.x : le tag v7 est déplacé à chaque correctifPratique ; acceptable pour les actions officielles de GitHub
actions/checkout@v7.0.1Une version préciseLe tag peut encore, en théorie, être déplacé
actions/checkout@<empreinte de 40 caractères>Un commit précisImmuable : la référence la plus sûre, recommandée pour les actions tierces
./.github/actions/preparerUne action définie dans ton dépôtSous ton contrôle

Les actions se trouvent sur le GitHub Marketplace. Avant d'en utiliser une, regarde qui la publie (badge Verified creator), si elle est maintenue, et préfère les actions officielles (actions/…, github/…, docker/…). Une action exécute son code avec accès à ton dépôt et à tes secrets (chapitre 7).

Expressions et contextes

Tout ce qui est écrit entre ${{ et }} est une expression, évaluée par GitHub avant l'exécution. Elle lit des contextes, des objets qui décrivent l'exécution en cours.

ContexteContenuExemple
githubL'événement, le dépôt, la branche, le commitgithub.ref_name (main), github.sha, github.event_name, github.actor
envLes variables définies avec env:env.NODE_VERSION
varsLes variables de configuration du dépôt ou de l'environnementvars.URL_RECETTE (chapitre 8)
secretsLes secretssecrets.DEPLOY_SSH_KEY
inputsLes paramètres d'un workflow_dispatch ou d'un workflow_callinputs.environnement
matrixLa combinaison en cours d'une matricematrix.node
stepsLes sorties et le résultat des étapes précédentes du jobsteps.tests.outcome, steps.version.outputs.numero
needsLes sorties et le résultat des jobs cités dans needsneeds.build.outputs.image
runnerLa machinerunner.os (Linux, Windows, macOS), runner.temp

Quelques fonctions utiles : contains(github.event.head_commit.message, '[skip deploy]'), startsWith(github.ref, 'refs/tags/v'), format('sorbier-frais:{0}', github.sha), hashFiles('package-lock.json') (une empreinte du fichier, utilisée pour les caches).

Dans un if, les accolades sont facultatives : if: github.ref == 'refs/heads/main' et if: ${{ github.ref == 'refs/heads/main' }} sont équivalents.

GitHub définit aussi des variables d'environnement sur chaque runner, lisibles dans les commandes : GITHUB_SHA (le commit), GITHUB_REF_NAME (la branche ou le tag), GITHUB_REPOSITORY (sorbier/sorbier-frais), GITHUB_WORKSPACE (le dossier du code), RUNNER_OS, et CI=true, que beaucoup d'outils détectent pour passer en mode non interactif.

Transmettre des données entre étapes et entre jobs

Trois fichiers spéciaux, dont le chemin est donné par des variables, permettent à une étape de communiquer :

FichierEffetExemple
$GITHUB_ENVDéfinit une variable d'environnement pour les étapes suivantes du jobecho "DATE_BUILD=$(date -u +%F)" >> "$GITHUB_ENV"
$GITHUB_OUTPUTDéfinit une sortie de l'étape, lisible par steps.<id>.outputs.<nom>echo "numero=2.4.0" >> "$GITHUB_OUTPUT"
$GITHUB_STEP_SUMMARYAjoute du Markdown au résumé affiché sur la page de l'exécutionecho "### 42 tests réussis" >> "$GITHUB_STEP_SUMMARY"

Pour passer une valeur à un autre job, on la déclare comme sortie du job :

yaml
jobs:
  version:
    runs-on: ubuntu-latest
    outputs:
      numero: ${{ steps.lire.outputs.numero }}      # sortie du job
    steps:
      - uses: actions/checkout@v7
      - id: lire
        run: echo "numero=$(node -p "require('./package.json').version")" >> "$GITHUB_OUTPUT"

  afficher:
    needs: version
    runs-on: ubuntu-latest
    steps:
      - run: echo "Version à livrer = ${{ needs.version.outputs.numero }}"

Permissions et GITHUB_TOKEN

À chaque exécution, GitHub crée un jeton temporaire, GITHUB_TOKEN, qui permet au workflow d'agir sur le dépôt : lire le code, commenter une pull request, publier un paquet. Il expire à la fin du job. Ses droits se règlent avec permissions, au niveau du workflow ou d'un job :

yaml
permissions:
  contents: read          # lire le code : suffit pour une CI

jobs:
  publier-image:
    permissions:
      contents: read
      packages: write     # pousser une image dans le registre de GitHub
    runs-on: ubuntu-latest
    steps: [...]

Dès que tu écris un bloc permissions, tout ce qui n'y figure pas passe à none. Donne le minimum à chaque job : si une action malveillante s'exécute, elle ne pourra faire que ce que le jeton permet (chapitre 8).

Éviter les exécutions en double : concurrency

Hugo pousse trois commits en deux minutes : sans précaution, trois pipelines complets tournent, dont deux déjà obsolètes. concurrency regroupe les exécutions et annule les anciennes :

yaml
concurrency:
  group: ci-${{ github.workflow }}-${{ github.ref }}   # un groupe par workflow et par branche
  cancel-in-progress: true                           # le nouveau lancement annule l'ancien

Pour un workflow de déploiement, garde cancel-in-progress: false : on ne coupe pas un déploiement au milieu, on attend qu'il finisse avant de lancer le suivant.

Les runners hébergés par GitHub et leur coût

Étiquette runs-onSystèmeQuand l'utiliser
ubuntu-latestLa dernière version d'Ubuntu proposée par GitHubPresque tout : Node.js, PHP, Python, Java, .NET, Docker
windows-latestWindows ServerApplications de bureau .NET (WinForms, WPF), scripts PowerShell spécifiques
macos-latestmacOSApplications iOS et macOS

Les étiquettes -latest changent de version quand GitHub met à jour ses images. Pour figer le système, utilise une étiquette numérotée (comme ubuntu-24.04) ; chaque image liste ses logiciels préinstallés dans le dépôt actions/runner-images.

Le coût : sur un dépôt public, les runners standards sont gratuits et illimités. Sur un dépôt privé, l'offre gratuite inclut chaque mois un crédit de calcul équivalent à environ 2 000 minutes Linux, et 500 Mo de stockage pour les artefacts. Une minute Windows coûte environ 1,7 fois une minute Linux, une minute macOS environ 10 fois : reste sur Linux quand le projet le permet. Le GitHub Student Developer Pack donne aux étudiants l'offre GitHub Pro, plus généreuse.

Suivre une exécution

L'onglet Actions du dépôt liste les exécutions de chaque workflow avec leur statut :

StatutSignification
En file d'attente (queued)En attente d'un runner libre
En cours (in progress)Une étape s'exécute ; son journal défile en direct
Réussi (success), coche verteToutes les étapes ont réussi
Échec (failure), croix rougeUne étape a renvoyé un code d'erreur
Annulé (cancelled)Annulé à la main ou par concurrency
Ignoré (skipped)Un if était faux, ou un job de needs a échoué

Clique sur un job pour déplier chaque étape et lire sa sortie. Le bouton Re-run jobs relance un job sans nouveau commit (Re-run failed jobs ne relance que ceux qui ont échoué), et la case Enable debug logging relance avec un journal détaillé. Le même suivi existe en ligne de commande avec l'outil gh : gh run list, gh run watch, gh run view --log-failed.

Le premier workflow complet de sorbier-frais

yaml
name: CI

on:
  push:
    branches: [main]
  pull_request:
  workflow_dispatch:

permissions:
  contents: read

concurrency:
  group: ci-${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

jobs:
  lint:
    name: Lint et formatage
    runs-on: ubuntu-latest
    timeout-minutes: 10
    steps:
      - uses: actions/checkout@v7
      - uses: actions/setup-node@v7
        with:
          node-version: 24
          cache: npm
      - run: npm ci
      - run: npx prettier --check .
      - run: npm run lint

  tests:
    name: Tests unitaires
    runs-on: ubuntu-latest
    timeout-minutes: 15
    steps:
      - uses: actions/checkout@v7
      - uses: actions/setup-node@v7
        with:
          node-version: 24
          cache: npm
      - run: npm ci
      - name: Lancer les tests
        run: npm test -- --ci
      - name: Résumé
        if: always()
        run: echo "Tests terminés pour le commit $GITHUB_SHA" >> "$GITHUB_STEP_SUMMARY"

lint et tests tournent en parallèle ; une pull request n'est fusionnable que si les deux sont verts, une fois la règle de protection configurée (chapitre 6). npm test -- --ci transmet l'option --ci à Jest, qui refuse alors d'écrire de nouveaux instantanés de test au lieu de les créer en silence.

Exercices

Exercice 1 sur 5 Écrire les déclencheurs Réussi À revoir

Écris le bloc on d'un workflow qui doit tourner :

  • à chaque pull request vers main ;
  • à chaque push sur main, sauf si seuls des fichiers Markdown ont changé ;
  • chaque lundi à 6 h 00 heure de Paris en hiver ;
  • quand on clique sur Run workflow.
Voir la réponseMasquer la réponse de l'exercice 1
yaml
on:
  pull_request:
    branches: [main]
  push:
    branches: [main]
    paths-ignore:
      - '**.md'
  schedule:
    - cron: '0 5 * * 1'
  workflow_dispatch:

Paris est à UTC+1 en hiver : 6 h 00 à Paris = 5 h 00 UTC. Le cinquième champ vaut 1 pour lundi. Sans timezone, cron compte en UTC : en été, ce workflow tournera à 7 h 00 heure de Paris. Mieux, depuis mars 2026 : - cron: '0 6 * * 1' suivi de timezone: Europe/Paris sur la ligne suivante, qui tourne à 6 h 00 à Paris toute l'année.

Tu avais trouvé ?

Exercice 2 sur 5 Corriger un workflow invalide Réussi À revoir

GitHub refuse ce workflow. Trouve les quatre erreurs.

yaml
name: CI
on:
  push:
    branches: main
jobs:
  tests:
    runs-on:ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - uses: actions/setup-python@v7
        with:
          python-version: 3.10
       - run: pip install -r requirements.txt
      -run: pytest
Voir la réponseMasquer la réponse de l'exercice 2
  1. runs-on:ubuntu-latest : il manque l'espace après les deux-points, la ligne n'est pas lue comme une clé. Écrire runs-on: ubuntu-latest.
  2. python-version: 3.10 est lu comme le nombre 3.1 : GitHub installerait Python 3.1, qui n'existe pas dans le catalogue, et l'étape échouerait. Écrire python-version: '3.10'.
  3. - run: pip install… est décalé d'un espace par rapport aux autres éléments de la liste : YAML signale une indentation incohérente.
  4. -run: pytest : il manque l'espace après le tiret. Écrire - run: pytest.

branches: main (sans crochets) n'est pas une erreur : GitHub accepte un texte seul à la place d'une liste d'un élément.

Tu avais trouvé ?

Exercice 3 sur 5 Quels jobs tournent ? Réussi À revoir

yaml
on:
  push:
    branches: [main]
  pull_request:
jobs:
  lint:
    runs-on: ubuntu-latest
    steps: [...]
  tests:
    runs-on: ubuntu-latest
    steps: [...]
  build:
    needs: [lint, tests]
    runs-on: ubuntu-latest
    steps: [...]
  deploy:
    needs: build
    if: github.event_name == 'push'
    runs-on: ubuntu-latest
    steps: [...]

Indique quels jobs s'exécutent, et dans quel ordre, dans chaque cas :

  1. Lina ouvre une pull request ; tout réussit.
  2. La pull request est fusionnée dans main ; tout réussit.
  3. Après une fusion dans main, le job tests échoue.
Voir la réponseMasquer la réponse de l'exercice 3
  1. lint et tests en parallèle, puis build. deploy est ignoré : l'événement est pull_request, pas push.
  2. La fusion crée un push sur main : lint et tests en parallèle, puis build, puis deploy.
  3. lint réussit, tests échoue ; build est ignoré (un job de needs a échoué) et deploy aussi, puisqu'il dépend de build. Rien n'est déployé.

Tu avais trouvé ?

Exercice 4 sur 5 Passer une valeur d'un job à l'autre Réussi À revoir

Le job build calcule l'étiquette de l'image Docker : sorbier-frais: suivi des sept premiers caractères du commit. Le job deploy doit l'afficher. Complète les deux jobs.

yaml
jobs:
  build:
    runs-on: ubuntu-latest
    # à compléter
  deploy:
    needs: build
    runs-on: ubuntu-latest
    # à compléter
Voir la réponseMasquer la réponse de l'exercice 4
yaml
jobs:
  build:
    runs-on: ubuntu-latest
    outputs:
      image: ${{ steps.etiquette.outputs.image }}
    steps:
      - id: etiquette
        run: echo "image=sorbier-frais:${GITHUB_SHA::7}" >> "$GITHUB_OUTPUT"
  deploy:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - run: echo "Image à déployer = ${{ needs.build.outputs.image }}"

${GITHUB_SHA::7} est une syntaxe de Bash qui garde les sept premiers caractères de la variable. La valeur passe par trois étapes : écrite dans $GITHUB_OUTPUT par l'étape etiquette, exposée comme sortie du job par outputs, lue par needs.build.outputs.image.

Tu avais trouvé ?

Exercice 5 sur 5 Repérer l'injection Réussi À revoir

Un workflow déclenché par issues (l'ouverture d'un ticket) contient cette étape :

yaml
      - run: echo "Nouveau ticket = ${{ github.event.issue.title }}" >> tickets.log
  1. Pourquoi est-ce dangereux ?
  2. Réécris l'étape de façon sûre.
Voir la réponseMasquer la réponse de l'exercice 5
  1. N'importe quel visiteur peut ouvrir un ticket et choisir son titre. L'expression est remplacée dans le texte du script avant l'exécution : un titre contenant des guillemets et des commandes shell ferait exécuter ces commandes sur le runner, avec le jeton du workflow. C'est une injection de script.
  2. On passe le titre par une variable d'environnement, que le shell traite comme une donnée :
yaml
      - env:
          TITRE: ${{ github.event.issue.title }}
        run: echo "Nouveau ticket = $TITRE" >> tickets.log

Tu avais trouvé ?

Chapitre terminé !

Fais les 5 exercices avant de passer à la suite : c'est en t'entraînant que tout se fixe.

chapitres
–
de série
–
du cours
–
Chapitre suivant : Matrice, cache et artefacts dans GitHub ActionsRevoir le sommaire du cours