Retour au cours

infra / github-actions

Cache de dépendances

Leçon 61 exercice

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-keys comme filet de sécurité quand la clé exacte n'existe pas encore
  • Préférer l'option cache: intégrée de setup-node/setup-python au 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émentRôle
Clé de cache (key:)Identifie un cache précis, généralement un hash de package-lock.json
restore-keysFilet de sécurité : récupère le cache le plus proche si la clé exacte n'existe pas
Option cache: de setup-nodeRaccourci 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

yaml
# 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"
yaml
# 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-
yaml
# 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-
yaml
# 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
bash
# 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/pip dans setup-node/setup-python couvre 90% des besoins simplement.
  • actions/cache offre un contrôle fin via key/restore-keys, basé sur un hash du lockfile.
  • restore-keys permet un cache "partiel" quand la clé exacte n'existe pas encore.
  • cache-from/to: type=gha accélère spécifiquement les builds d'images Docker en CI.

Exercices pratiques

1 disponible
1

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.

Résoudre l’exercice →