infra / github-actions
Artifacts
Explication
Ce que vous allez apprendre
- Comprendre pourquoi chaque job démarre isolé, sans rien de ce que le job précédent a produit
- Sauvegarder des fichiers entre jobs avec
upload-artifactetdownload-artifact - Distinguer un artifact (résultat unique d'une exécution) d'un cache (réinstallation accélérée)
- Sauvegarder spécifiquement un artifact en cas d'échec avec
if: failure() - Éviter le piège d'un upload silencieusement vide avec
if-no-files-found: error
Dans quel contexte ?
Un pipeline compile une application dans un job build, puis un job séparé test doit exécuter des tests sur le résultat compilé. Comme ces deux jobs tournent sur des machines totalement différentes par défaut, le job test ne trouve rien : les fichiers compilés par build ont simplement disparu à la fin de son exécution. Les artifacts existent exactement pour ce besoin : transporter un résultat de build d'un job à un autre au sein du même workflow.
Le problème : chaque job démarre isolé
D'abord, un workflow GitHub Actions peut être découpé en plusieurs jobs qui s'exécutent potentiellement sur des machines différentes, par exemple un job qui compile puis un job séparé qui teste le résultat compilé.
Ce que ça implique concrètement
Chaque job démarre sur une machine fraîche, sans rien de ce que le job précédent a produit.
La conséquence sans mécanisme dédié
Sans mécanisme dédié, le résultat d'un job, des fichiers compilés, un rapport, disparaît simplement à la fin de son exécution.
La solution : l'artifact
Un artifact est un ensemble de fichiers explicitement sauvegardé à la fin d'un job, avec upload-artifact, que n'importe quel autre job du même workflow peut ensuite récupérer, avec download-artifact.
| Artifact | Cache | |
|---|---|---|
| Contenu | Résultat de build unique, propre à cette exécution | Dépendances réinstallables à l'identique |
| But | Transporter un résultat entre jobs | Accélérer une réinstallation |
| Durée de vie typique | Le temps du workflow (ou plus, configurable) | Réutilisé d'une exécution à l'autre |
Ce qui distingue un artifact d'un cache
C'est différent du cache de dépendances vu précédemment : le cache sert à accélérer une réinstallation identique, alors qu'un artifact transporte un résultat de build unique et précis, propre à cette exécution.
Prérequis
Cette leçon suppose la coordination entre jobs (needs:) déjà vue en leçon 4 : un artifact uploadé par un job n'est utile que si un autre job, dépendant du premier, sait le télécharger au bon moment.
Un usage souvent oublié : garder une preuve en cas d'échec
Uploader un artifact n'est pas réservé au cas de succès : la condition if: failure() permet de sauvegarder spécifiquement un rapport de test ou des logs détaillés uniquement quand quelque chose s'est mal passé.
Pourquoi c'est utile
C'est extrêmement utile pour diagnostiquer un échec après coup, sans avoir à reproduire le problème localement.
Piège fréquent
Sans if-no-files-found: error, un upload-artifact qui ne trouve aucun fichier correspondant au chemin donné se termine SILENCIEUSEMENT en succès. Ça peut masquer un vrai problème en amont (le build n'a rien produit) derrière un pipeline qui semble pourtant "vert" — un faux sentiment de sécurité à éviter absolument.
Un piège de fiabilité à connaître
Sans if-no-files-found: error, un upload qui ne trouve aucun fichier correspondant au chemin donné se termine silencieusement en succès.
Ce que ce silence peut masquer
Cela peut masquer un vrai problème en amont, le build n'a rien produit, derrière un pipeline qui semble pourtant "vert". Rendre cet échec explicite évite un faux sentiment de sécurité.
La leçon suivante s'attaque à un problème d'un autre ordre : éviter de dupliquer la même configuration entre plusieurs workflows, avec les reusable workflows.
Commandes & code
Artifacts
Les artifacts transportent des fichiers d'un job à un autre, ou les rendent téléchargeables après l'exécution.
name: Build and Test
on: push
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci && npm run build
- name: Upload build artifact
uses: actions/upload-artifact@v4
with:
name: dist-files
path: dist/
retention-days: 7 # durée de rétention (défaut 90 jours, coûte du stockage)
test-e2e:
needs: build
runs-on: ubuntu-latest
steps:
- name: Download build artifact
uses: actions/download-artifact@v4
with:
name: dist-files
path: dist/
- run: npx playwright test
- name: Upload rapport de test en cas d'échec
if: failure()
uses: actions/upload-artifact@v4
with:
name: playwright-report
path: playwright-report/
retention-days: 14# Artifact multi-fichiers avec exclusion de patterns
- uses: actions/upload-artifact@v4
with:
name: coverage-report
path: |
coverage/
!coverage/**/*.tmp
if-no-files-found: error # échoue explicitement si rien à uploader (évite un faux succès)# Récupérer un artifact depuis le terminal, sans passer par l'UI
gh run download <run-id> --name dist-files
gh api repos/:owner/:repo/actions/artifacts# Publier un artifact comme "release asset" une fois le workflow terminé
- name: Attacher à la release GitHub
if: startsWith(github.ref, 'refs/tags/')
uses: softprops/action-gh-release@v2
with:
files: dist/app-${{ github.ref_name }}.tar.gzRésumé
upload-artifact/download-artifactfont transiter des fichiers entre jobs d'un même workflow.retention-daysmaîtrise le coût de stockage (défaut 90 jours, souvent excessif).if-no-files-found: errorévite un succès silencieux quand rien n'a été généré.gh run downloadrécupère un artifact en ligne de commande, sans ouvrir l'UI GitHub.
Exercices pratiques
Mission : un job de tests qui ne trouve jamais les fichiers compilés
Objectif : Diagnostiquer une chaîne artifact incomplète entre deux jobs, et sécuriser l'upload contre un échec silencieux.
Contexte
Le job build compile l'application dans dist/ puis l'uploade avec upload-artifact (name: dist-files, path: dist/). Le step d'upload apparaît pourtant coché vert dans les logs. Le job test-e2e, qui en dépend via needs: build, échoue systématiquement en ne trouvant aucun fichier dans dist/ au moment de lancer Playwright — comme si l'artifact n'avait jamais existé.