Aller au contenu

Optimiser les workflows GitHub Actions

Mise à jour :

Un workflow lent, c’est du temps perdu pour toute l’équipe. Entre l’attente des résultats de tests et les déploiements qui traînent, une CI mal optimisée peut coûter des heures chaque semaine.

Ce guide vous montre comment réduire drastiquement le temps d’exécution de vos workflows GitHub Actions.

Pourquoi optimiser ?

ProblèmeImpactSolution
Installation des dépendances à chaque run+2-5 minCache
Téléchargement d’artifacts volumineux+1-3 minArtifacts optimisés
Workflows parallèles inutilesGaspillage de runnersConcurrency
Debug en aveugleTemps perdu à investiguerLogs structurés

Guides par sujet

Debug workflow

Techniques pour diagnostiquer les workflows lents ou défaillants. Lire le guide →

Gains rapides

1. Activer le cache intégré (setup-node, setup-python)

La plupart des actions setup-* ont un cache intégré :

# ❌ Pas de cache : npm ci à chaque run (30-60s)
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: '20'
- run: npm ci
# ✅ Avec cache : npm ci sauté si cache hit (2-5s)
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: '20'
cache: 'npm' # Active le cache !
- run: npm ci

2. Éviter les runs simultanés inutiles

# Annule les runs précédents sur la même branche
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

Gain : évite de gaspiller des runners sur des commits intermédiaires.

3. Checkout partiel

# ❌ Clone tout l'historique
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
# ✅ Clone uniquement le dernier commit (plus rapide)
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 1

Exception : gardez fetch-depth: 0 si vous avez besoin des tags ou de l’historique (semantic-release, changelogs).

4. Paralléliser les jobs indépendants

jobs:
# Ces jobs s'exécutent en parallèle
lint:
runs-on: ubuntu-latest
steps: [...]
test:
runs-on: ubuntu-latest
steps: [...]
security:
runs-on: ubuntu-latest
steps: [...]
# Ce job attend les autres
deploy:
needs: [lint, test, security]
runs-on: ubuntu-latest
steps: [...]

5. Filtrer les déclenchements

on:
push:
branches: [main]
paths:
- 'src/**'
- 'package*.json'
- '!**/*.md' # Ignore les changements de docs

Métriques à surveiller

Temps d’exécution moyen

Dans l’onglet Actions, regardez la durée moyenne de vos workflows. Objectifs raisonnables :

Type de workflowDurée cible
Lint< 1 min
Tests unitaires< 5 min
Build< 5 min
Tests E2E< 15 min
Déploiement< 5 min

Taux de cache hit

Vérifiez dans les logs si le cache est utilisé :

Cache restored from key: npm-linux-abc123...

Un taux de cache hit < 50% indique un problème de clé de cache.

Coût (minutes utilisées)

Repository → Settings → Actions → Usage and billing

Surveillez la consommation, surtout avec les runners macOS (10x plus cher).

Patterns anti-performance

❌ Recalculer ce qui n’a pas changé

# Inefficace : build complet à chaque commit
- run: npm run build
- run: npm test

Solution : utilisez le cache pour les dépendances et le build incrémental.

❌ Jobs séquentiels sans raison

# Inefficace : lint attend test qui attend build
jobs:
build:
steps: [...]
test:
needs: build # Pourquoi ?
steps: [...]
lint:
needs: test # Vraiment nécessaire ?
steps: [...]

Solution : parallélisez les jobs indépendants.

❌ Télécharger les mêmes artifacts plusieurs fois

# Inefficace : chaque job télécharge tout
jobs:
test-unit:
steps:
- run: npm ci # Télécharge 500MB
test-integration:
steps:
- run: npm ci # Re-télécharge 500MB
test-e2e:
steps:
- run: npm ci # Re-télécharge 500MB

Solution : job de build partagé + artifacts ou cache.

❌ Images Docker non optimisées

# Inefficace : image lourde avec outils inutiles
container:
image: ubuntu:latest
# Mieux : image légère avec juste ce qu'il faut
container:
image: node:20-alpine

Workflow optimisé (exemple complet)

name: CI
on:
push:
branches: [main]
paths-ignore:
- '**.md'
- 'docs/**'
pull_request:
branches: [main]
paths-ignore:
- '**.md'
# Annule les runs précédents sur la même PR/branche
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
jobs:
# Jobs parallèles rapides
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 1
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- run: npm run lint
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 1
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- run: npm test
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 1
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- run: npm run build
# Upload du build pour les jobs suivants
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: build
path: dist/
retention-days: 1
# Job final qui attend tous les autres
deploy:
needs: [lint, test, build]
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1
with:
name: build
path: dist/
- run: ./deploy.sh

Liens utiles