infra / github-actions
Cache de dépendances
Explication
Ce que vous allez apprendre
- Comprendre pourquoi chaque exécution GitHub Actions démarre sur une machine "propre"
- Construire une clé de cache basée sur un hachage du fichier de verrouillage des dépendances
- Utiliser
restore-keyscomme filet de sécurité quand la clé exacte n'existe pas encore - Préférer l'option
cache:intégrée desetup-node/setup-pythonau cache manuel - Mesurer le gain de temps concret que le cache apporte sur un pipeline
Dans quel contexte ?
Un projet Node.js avec plus de 800 paquets dans node_modules voit chaque exécution de sa CI passer 2 des 3 minutes totales rien qu'à réinstaller ces dépendances depuis zéro, alors qu'elles n'ont pas changé depuis des semaines. Sur 50 exécutions par jour, ça représente des heures de calcul gaspillées, et une attente frustrante pour chaque développeur qui pousse du code. Le cache de dépendances règle précisément ce problème : réutiliser ce qui a déjà été installé tant que rien n'a changé.
Le problème : repartir de zéro à chaque exécution
D'abord, chaque exécution d'un workflow GitHub Actions démarre sur une machine "propre", sans rien de ce qui a été installé précédemment.
Ce que ça implique sans précaution
Sans précaution, cela signifie retélécharger et réinstaller toutes les dépendances du projet à chaque fois, un gaspillage de temps qui peut représenter la majorité de la durée d'un job.
L'idée du cache
Le cache consiste à sauvegarder, après une exécution, le résultat d'une installation coûteuse, pour le restaurer directement lors de la prochaine exécution si rien n'a changé.
Le gain concret
C'est un gain de temps considérable dès que les dépendances évoluent moins souvent que le code lui-même.
| Élément | Rôle |
|---|---|
Clé de cache (key:) | Identifie un cache précis, généralement un hash de package-lock.json |
restore-keys | Filet de sécurité : récupère le cache le plus proche si la clé exacte n'existe pas |
Option cache: de setup-node | Raccourci intégré qui couvre déjà la majorité des besoins courants |
Prérequis
Cette leçon suppose le premier workflow (leçon 2) déjà en place : le cache s'ajoute à une étape d'installation de dépendances déjà existante, il ne la remplace pas.
La question centrale : comment savoir qu'un cache est encore valide
Un cache n'est utile que s'il est réutilisé quand c'est légitime, et invalidé quand ce ne l'est plus. La "clé" de cache sert exactement à ça.
Comment construire une bonne clé
En la construisant à partir d'un hachage du fichier de verrouillage des dépendances, package-lock.json par exemple, on garantit que la clé change automatiquement dès que les dépendances changent.
Un filet de sécurité pour un cache "proche"
Si la clé exacte n'existe pas encore, lors d'une première exécution avec de nouvelles dépendances, restore-keys permet de récupérer quand même le cache le plus récent qui correspond à un préfixe de clé plus général.
Bonne pratique
Avant d'écrire une configuration de cache manuelle avec actions/cache@v4, vérifie si setup-node, setup-python ou l'action équivalente pour ton langage propose déjà une option cache: intégrée — c'est souvent suffisant et bien plus simple à maintenir.
Pourquoi ce filet reste utile
Ce n'est pas parfait, mais souvent bien plus rapide que de repartir de zéro, car la majorité des dépendances n'a probablement pas changé.
Le raccourci pratique à privilégier
Avant de configurer du cache manuel, les actions officielles setup-node/setup-python proposent une option cache: intégrée qui couvre déjà la majorité des besoins courants.
La leçon suivante s'attaque à un autre problème fréquent : protéger les valeurs sensibles utilisées par un pipeline, avec les secrets et variables.
Commandes & code
Cache de dépendances
# Méthode simple : le cache intégré à setup-node/setup-python (recommandé en premier réflexe)
- uses: actions/setup-node@v4
with:
node-version: "20"
cache: "npm" # met en cache ~/.npm automatiquement selon package-lock.json
- uses: actions/setup-python@v5
with:
python-version: "3.12"
cache: "pip"# Cache manuel et explicite avec actions/cache : contrôle total de la clé et du chemin
- name: Cache node_modules
uses: actions/cache@v4
with:
path: |
~/.npm
node_modules
key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-npm-# Cache d'un cache de build plus lourd (ex: Docker layer cache, Gradle, Cargo)
- name: Cache Cargo
uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-# Cache des layers Docker via Buildx (accélère fortement les builds répétés)
- uses: docker/setup-buildx-action@v3
- uses: docker/build-push-action@v6
with:
context: .
push: false
cache-from: type=gha
cache-to: type=gha,mode=max# La clé DOIT changer quand les dépendances changent, sinon un cache périmé est réutilisé
# hashFiles('**/package-lock.json') garantit un cache-miss automatique dès que le lockfile change
# Gérer/purger les caches manuellement
gh cache list
gh cache delete <cache-id>Résumé
cache: npm/pipdanssetup-node/setup-pythoncouvre 90% des besoins simplement.actions/cacheoffre un contrôle fin viakey/restore-keys, basé sur un hash du lockfile.restore-keyspermet un cache "partiel" quand la clé exacte n'existe pas encore.cache-from/to: type=ghaaccélère spécifiquement les builds d'images Docker en CI.
Exercices pratiques
Mission : un cache qui sert de vieilles dépendances
Objectif : Diagnostiquer une clé de cache mal construite qui réutilise un cache périmé, puis la corriger avec hashFiles et restore-keys.
Contexte
Un projet utilise actions/cache avec une clé fixe : key: ${{ runner.os }}-npm, sans référence au fichier de verrouillage des dépendances. Après avoir mis à jour une dépendance dans package-lock.json, des bugs subtils apparaissent uniquement en CI, jamais en local. En creusant, tu réalises que le cache restauré contient encore d'anciennes versions des paquets.