infra / github-actions
Jobs et steps : dépendances, sorties, conditions
Explication
Ce que vous allez apprendre
- Comprendre pourquoi les jobs d'un workflow s'exécutent en parallèle et isolés par défaut
- Coordonner explicitement des jobs dépendants les uns des autres avec
needs: - Faire communiquer deux jobs isolés via des
outputs - Ajuster le comportement par défaut en cas d'échec avec
continue-on-erroretif: always() - Distinguer
$GITHUB_ENV(même job) de$GITHUB_OUTPUT(inter-jobs vianeeds)
Dans quel contexte ?
Un pipeline a deux jobs : build, qui compile l'application, et deploy, qui la met en production. Sans coordination explicite entre les deux, GitHub Actions les lance en parallèle par défaut — et il est déjà arrivé qu'un déploiement parte alors que le build venait tout juste d'échouer, les deux jobs ayant démarré au même instant sur des machines totalement isolées l'une de l'autre. Cette leçon montre comment forcer un ordre logique entre jobs, et comment leur faire échanger des informations malgré leur isolement.
Un rappel important : les jobs s'ignorent par défaut
D'abord, rappelle-toi de la première leçon : les jobs d'un même workflow s'exécutent en parallèle par défaut, chacun sur une machine isolée qui ne partage rien avec les autres.
Le problème que ça pose
C'est très bien pour la rapidité, mais problématique dès qu'un job dépend logiquement d'un autre : on ne veut pas déployer une application dont le build vient d'échouer.
La solution : une coordination explicite
needs: introduit cette coordination explicite entre jobs par ailleurs isolés.
| Mécanisme | Portée | Rôle |
|---|---|---|
needs: | Entre jobs | Force un ordre d'exécution et attend le succès du job précédent |
outputs + needs.<job>.outputs.<cle> | Entre jobs | Transmet une valeur calculée d'un job à un autre |
$GITHUB_ENV | À l'intérieur d'un même job | Propage une variable aux steps suivants du même job |
continue-on-error / if: always() | Au sein d'un step/job | Ajuste le comportement par défaut en cas d'échec |
Un nouveau problème : comment communiquer entre jobs
Puisque les jobs ne partagent pas de mémoire ni de système de fichiers, comment un job "déploiement" peut-il savoir ce qui s'est passé dans le job "test" qui l'a précédé ?
La solution : les outputs
Les outputs résolvent ce problème : un job peut explicitement exposer une valeur calculée pendant son exécution, que les jobs suivants pourront lire via needs.<job>.outputs.<clé>.
Prérequis
Cette leçon suppose la première leçon du cours acquise (vocabulaire workflow/job/step) : elle approfondit spécifiquement la coordination entre plusieurs jobs d'un même workflow.
Le comportement par défaut en cas d'échec
Par défaut, l'échec d'un step interrompt immédiatement tout le reste du job : les steps suivants ne s'exécutent pas. C'est un comportement protecteur, mais parfois trop strict.
Deux façons d'ajuster ce comportement
On veut parfois continuer malgré un échec ponctuel, avec continue-on-error, ou au contraire garantir qu'une étape de nettoyage s'exécute toujours, même après un échec, avec if: always().
Ce que ça change pour ton pipeline
Savoir manier ces conditions transforme un pipeline rigide en un pipeline qui réagit intelligemment à ce qui se passe réellement.
Piège fréquent
$GITHUB_ENV et $GITHUB_OUTPUT se ressemblent mais ne servent pas au même besoin : le premier propage une variable aux steps suivants À L'INTÉRIEUR du même job, le second expose une valeur consultable depuis d'autres jobs via needs. Utiliser l'un à la place de l'autre fait échouer silencieusement la transmission de données attendue.
Une confusion fréquente à éviter
$GITHUB_ENV et $GITHUB_OUTPUT répondent à deux besoins différents malgré leur ressemblance.
La distinction précise entre les deux
Le premier propage une variable aux steps suivants à l'intérieur du même job, le second expose une valeur consultable depuis d'autres jobs via needs.
La leçon suivante s'attaque à un problème différent : éviter de dupliquer un même job pour tester plusieurs combinaisons, avec les matrix builds.
Commandes & code
Jobs & steps
name: Build and Deploy
on: push
jobs:
test:
runs-on: ubuntu-latest
outputs:
coverage: ${{ steps.tests.outputs.coverage_pct }}
steps:
- uses: actions/checkout@v4
- id: tests
run: |
npm ci
npm test
echo "coverage_pct=87" >> "$GITHUB_OUTPUT"
build:
needs: test # attend que "test" réussisse avant de démarrer
runs-on: ubuntu-latest
if: needs.test.outputs.coverage >= 80
steps:
- uses: actions/checkout@v4
- run: npm run build
deploy:
needs: [test, build] # dépend de PLUSIEURS jobs
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
steps:
- run: echo "Déploiement en production"# Steps : gestion des échecs et conditions fines
jobs:
ci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Tests unitaires
id: unit
run: npm run test:unit
continue-on-error: true # n'interrompt pas le job en cas d'échec
- name: Notifier si les tests unitaires ont échoué
if: steps.unit.outcome == 'failure'
run: echo "::warning::Les tests unitaires ont échoué, voir les logs"
- name: Étape toujours exécutée (cleanup, notif)
if: always()
run: echo "Nettoyage final"
- name: Étape seulement si tout a réussi
if: success()
run: echo "Tout est vert"# GITHUB_OUTPUT, GITHUB_ENV : fichiers spéciaux pour faire circuler des données entre steps/jobs
echo "build_id=$(date +%s)" >> "$GITHUB_ENV" # dispo dans les steps SUIVANTS du même job via $build_id
echo "artifact_name=app-${{ github.sha }}" >> "$GITHUB_OUTPUT"Résumé
needs:ordonne les jobs et permet de lire leursoutputs(transmis via$GITHUB_OUTPUT).- Par défaut, un échec de step arrête le job ;
continue-on-error: truechange ce comportement. if: always()exécute un step même après un échec (nettoyage, notification).$GITHUB_ENVpropage une variable aux steps suivants DANS le même job.
Exercices pratiques
Mission : un déploiement qui démarre avant la fin du build
Objectif : Corriger un workflow à deux jobs parallèles pour que le déploiement attende le succès du build et récupère sa sortie.
Contexte
Un pipeline a deux jobs, build et deploy, sans coordination entre eux. Comme les jobs s'exécutent en parallèle par défaut, il est déjà arrivé que deploy démarre alors que build venait tout juste d'échouer. De plus, build calcule un identifiant de build (build_id) que deploy a besoin de connaître pour nommer l'artefact déployé, mais ne dispose actuellement d'aucun moyen de le récupérer.