GitHub Actions : Workflow
Un workflow GitHub Actions est un fichier YAML placé dans .github/workflows/. Il orchestre des jobs qui s'exécutent sur des runners en réponse à des événements. Comprendre les primitives disponibles (matrix, container, artefacts, conditions) permet de construire des pipelines maintenables sans duplication.
Structure d'un job
Chaque job s'exécute dans un environnement indépendant. Les steps d'un même job partagent le filesystem du runner ; deux jobs distincts ne partagent rien par défaut.
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build
run: make build
runs-on sélectionne le runner. Pour un runner auto-hébergé avec un label personnalisé :
runs-on: [self-hosted, linux, gpu]
Container
Par défaut, les steps s'exécutent directement sur le runner. Spécifier un container fait tourner toutes les steps dans un conteneur Docker, utile pour garantir un environnement reproductible indépendamment du runner :
jobs:
test:
runs-on: self-hosted
container:
image: python:3.12-slim
steps:
- uses: actions/checkout@v4
- run: pip install pytest && pytest
Sans container, le job dépend des outils installés sur la machine hôte. Avec container, l'environnement est défini par l'image, portable et prévisible.
Matrix
strategy.matrix génère automatiquement plusieurs exécutions d'un job à partir d'une combinaison de variables. Utile pour tester sur plusieurs versions de langage ou systèmes d'exploitation :
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.10", "3.11", "3.12"]
steps:
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- run: pytest
Avec 3 versions, GitHub crée 3 jobs parallèles. Par défaut (fail-fast: true), l'échec d'une combinaison annule les autres jobs en cours de la matrice ; fail-fast: false laisse toutes les combinaisons aller au bout, ce qui donne une vue complète des versions incompatibles. max-parallel limite le nombre de jobs simultanés. Pour une matrice à deux dimensions :
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
python-version: ["3.11", "3.12"]
4 combinaisons → 4 jobs parallèles. matrix.include ajoute des combinaisons spécifiques, matrix.exclude en supprime :
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest]
python-version: ["3.11", "3.12"]
exclude:
- os: windows-latest
python-version: "3.11"
include:
- os: ubuntu-latest
python-version: "3.13"
experimental: true # variable supplémentaire propre à cette combinaison
Artefacts
Les artefacts permettent de partager des fichiers entre jobs ou de les conserver après l'exécution du workflow (logs, rapports de tests, binaires compilés).
Upload :
- name: Upload coverage report
uses: actions/upload-artifact@v4
with:
name: coverage-report
path: htmlcov/
retention-days: 7
Download dans un job suivant :
- name: Download coverage report
uses: actions/download-artifact@v4
with:
name: coverage-report
path: ./coverage
Les artefacts persistent après la fin du workflow (90 jours par défaut, sauf retention-days ou réglage du dépôt) et sont téléchargeables depuis l'onglet Actions du dépôt. Le job qui télécharge doit déclarer le job producteur dans needs, sans quoi les deux jobs démarrent en parallèle et l'artefact n'existe pas encore. Depuis la v4 des actions, un artefact est immuable : deux uploads sous le même nom dans une même exécution échouent, ce qui impose des noms distincts dans une matrice (par exemple coverage-${{ matrix.python-version }}).
Dépendances et conditions
needs impose un ordre d'exécution entre jobs. Sans needs, tous les jobs démarrent en parallèle :
jobs:
build:
runs-on: ubuntu-latest
steps:
- run: make build
deploy:
needs: build
runs-on: ubuntu-latest
steps:
- run: make deploy
if conditionne l'exécution d'un job ou d'une step :
deploy:
needs: build
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
Sans fonction de statut, une condition if inclut implicitement success() : le job ou la step ne s'exécute que si tout ce qui précède a réussi. Les autres fonctions modifient ce comportement :
| Fonction | Exécution |
|---|---|
success() | tout ce qui précède a réussi (défaut) |
failure() | au moins un job ou une step précédent a échoué (notifications d'échec) |
always() | dans tous les cas, y compris après annulation du workflow |
!cancelled() | dans tous les cas sauf annulation ; préférable à always() pour le nettoyage et les rapports |
Permissions du GITHUB_TOKEN
Chaque exécution reçoit automatiquement un GITHUB_TOKEN avec des permissions par défaut. Les permissions se réduisent au minimum nécessaire :
permissions:
contents: read
packages: write
pull-requests: write
Les permissions peuvent se définir au niveau du workflow (globales) ou du job (locales, plus précises). Toute permission absente d'un bloc permissions est fixée à none.
Variables d'environnement
env définit des variables à plusieurs niveaux :
env:
NODE_ENV: production # niveau workflow : disponible partout
jobs:
build:
env:
BUILD_FLAGS: "--minify" # niveau job
steps:
- run: echo $BUILD_FLAGS
env:
DEBUG: "true" # niveau step : prioritaire sur les niveaux supérieurs
$GITHUB_OUTPUT permet à une step d'exporter une valeur vers les steps suivantes du même job :
- uses: actions/checkout@v4
with:
fetch-depth: 0 # historique complet et tags, requis par git describe
- name: Get version
id: version
run: echo "tag=$(git describe --tags)" >> "$GITHUB_OUTPUT"
- name: Use version
run: echo "Deploying ${{ steps.version.outputs.tag }}"
Par défaut, actions/checkout effectue un clone superficiel (fetch-depth: 1) sans tags : git describe échouerait. Pour transmettre une valeur à un autre job, la sortie de step doit être remontée au niveau du job via outputs, puis lue avec needs.<job>.outputs.<nom> :
jobs:
version:
runs-on: ubuntu-latest
outputs:
tag: ${{ steps.version.outputs.tag }}
steps:
- id: version
run: echo "tag=v1.2.3" >> "$GITHUB_OUTPUT"
deploy:
needs: version
runs-on: ubuntu-latest
steps:
- run: echo "Deploying ${{ needs.version.outputs.tag }}"