Jobs, steps et runners
Job vs step : quelle différence ?
Dans GitHub Actions, un workflow contient des jobs, et chaque job contient des steps. Ce n'est pas la même chose :
- Un job s'exécute sur une machine virtuelle dédiée (un runner). Chaque job démarre sur un environnement propre et neuf.
- Un step est une étape individuelle à l'intérieur d'un job. Tous les steps d'un même job partagent le même système de fichiers et le même environnement.
Conséquence directe : si tu veux qu'une étape utilise un fichier créé par une étape précédente, ils doivent être dans le même job. Entre jobs différents, les fichiers ne sont pas partagés automatiquement.
Un step peut être une commande shell (run:) ou une action réutilisable du marketplace (uses:). Les deux coexistent librement dans un même job.
Les runners : où s'exécute ton code ?
Un runner est la machine virtuelle mise à disposition par GitHub pour exécuter tes jobs. Tu choisis le système d'exploitation avec la clé runs-on.
Runners hébergés par GitHub (GitHub-hosted)
ubuntu-latest— Linux Ubuntu, le plus rapide et le plus utilisé. Idéal pour CI standard.windows-latest— Windows Server. Utile pour les apps .NET, Electron ou les scripts PowerShell.macos-latest— macOS. Nécessaire pour compiler des apps iOS/macOS. Plus lent et plus cher en minutes.
Les minutes GitHub Actions sont gratuites pour les dépôts publics. Pour les dépôts privés, les runners macOS consomment 10× plus de minutes que Linux, et Windows 2× plus. Utilise ubuntu-latest par défaut sauf besoin spécifique.
Self-hosted runners
Tu peux aussi connecter ta propre machine comme runner. Utilise runs-on: self-hosted ou un tableau de labels comme runs-on: [self-hosted, linux, x64]. C'est utile pour des raisons de performance, de coût ou d'accès à des ressources internes.
Jobs en parallèle (comportement par défaut)
Par défaut, tous les jobs d'un workflow s'exécutent en parallèle. C'est idéal pour gagner du temps : lancer les tests frontend et backend en même temps, par exemple.
name: CI parallèle
on: push
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Lint du code
run: npm run lint
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Tests unitaires
run: npm test
# lint et test démarrent en même temps — pas de dépendance entre eux
Jobs séquentiels avec needs:
Pour qu'un job attende qu'un autre soit terminé, utilise la clé needs:. C'est indispensable pour un pipeline de déploiement : on ne déploie que si les tests passent.
name: Pipeline CI
on: push
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Vérification du code
run: npm run lint
test:
runs-on: ubuntu-latest
needs: lint # démarre seulement si lint réussit
steps:
- uses: actions/checkout@v4
- name: Tests
run: npm test
build:
runs-on: ubuntu-latest
needs: test # démarre seulement si test réussit
steps:
- uses: actions/checkout@v4
- name: Build production
run: npm run build
# Ordre d'exécution : lint → test → build (chaîne séquentielle)
needs: accepte aussi un tableau : needs: [lint, test] si un job doit attendre plusieurs jobs en parallèle avant de démarrer.
Réutiliser les actions du marketplace
Au lieu de réinventer la roue, GitHub met à disposition des milliers d'actions prêtes à l'emploi sur le marketplace. Les deux indispensables pour un projet Node.js :
actions/checkout@v4— clone ton dépôt sur le runner. Presque toujours en premier step.actions/setup-node@v4— installe Node.js dans la version souhaitée et configure le cache npm.
jobs:
test:
runs-on: ubuntu-latest
steps:
# 1. Cloner le dépôt
- name: Checkout du code
uses: actions/checkout@v4
# 2. Installer Node.js 20 avec cache npm
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm' # met en cache node_modules entre les runs
# 3. Installer les dépendances
- name: Installation des dépendances
run: npm ci
# 4. Lancer les tests
- name: Tests
run: npm test
Toujours épingler les actions avec une version précise (@v4) plutôt que @main ou @latest. Cela évite les régressions si l'action est mise à jour avec un changement incompatible.
Passer des données entre steps : outputs et env
Les steps d'un même job peuvent se partager des données de deux façons :
Variables d'environnement avec env:
Tu peux définir des variables d'environnement au niveau du job entier (disponibles dans tous ses steps) ou d'un step spécifique.
jobs:
build:
runs-on: ubuntu-latest
env:
NODE_ENV: production # disponible dans tous les steps
steps:
- uses: actions/checkout@v4
- name: Générer la version
run: |
VERSION=$(node -p "require('./package.json').version")
# Écrire dans $GITHUB_ENV pour partager avec les steps suivants
echo "APP_VERSION=$VERSION" >> $GITHUB_ENV
- name: Utiliser la version
run: echo "Déploiement version $APP_VERSION"
Outputs entre steps et entre jobs
Pour partager une valeur calculée entre jobs (pas seulement entre steps), utilise le mécanisme outputs :
jobs:
prepare:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.get-version.outputs.version }}
steps:
- id: get-version
run: |
VERSION=$(node -p "require('./package.json').version")
echo "version=$VERSION" >> $GITHUB_OUTPUT
deploy:
runs-on: ubuntu-latest
needs: prepare
steps:
- name: Déployer
run: echo "Version ${{ needs.prepare.outputs.version }}"
Les conditions avec if:
Tu peux conditionner l'exécution d'un step ou d'un job entier avec la clé if:. Quelques expressions courantes :
jobs:
deploy:
runs-on: ubuntu-latest
# Ce job tourne seulement sur la branche main
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- name: Build
run: npm run build
# Step qui tourne seulement si les étapes précédentes ont réussi
- name: Déployer en prod
if: success()
run: ./deploy.sh
# Step qui tourne UNIQUEMENT en cas d'échec — pour notifier
- name: Notifier en cas d'erreur
if: failure()
run: echo "Le déploiement a échoué !"
Les fonctions de statut les plus utiles dans if: :
success()— vrai si tous les steps précédents ont réussi (comportement par défaut)failure()— vrai si au moins un step précédent a échouéalways()— tourne toujours, même si le job est annulécancelled()— vrai si le workflow a été annulé manuellement
Le contexte github donne accès à plein d'informations sur le déclencheur : github.ref (branche/tag), github.actor (qui a déclenché), github.event_name (push, pull_request…), github.sha (commit SHA).
- Job = machine virtuelle dédiée · Step = étape dans un job (même système de fichiers).
runs-on: ubuntu-latestpour la grande majorité des cas — rapide et gratuit.- Sans
needs:, les jobs tournent en parallèle. Avecneeds: autrejob, ils sont séquentiels. actions/checkout@v4etactions/setup-node@v4sont les actions de base pour tout projet JS/TS.- Partage de données :
$GITHUB_ENVpour les variables,$GITHUB_OUTPUTpour les outputs inter-jobs. if: failure()pour les steps de notification d'erreur ·if: github.ref == 'refs/heads/main'pour restreindre au déploiement.