Tuto CI pour GitHub
Mettre en place une intégration continue sur GitHub Actions : workflows, jobs, cache, matrice, secrets, déploiement conditionnel. Tout ce qu'il faut pour sécuriser ton main.
À la fin du cours, tu sais
- Écrire un workflow GitHub Actions propre et lisible
- Mettre en place tests, lint, couverture en CI
- Gérer secrets et permissions avec le principe du moindre privilège
- Optimiser une CI lente (cache, parallélisme, concurrency)
- Aller du CI au CD avec environments et déploiement conditionnel
Prérequis
- Connaître Git et GitHub (pull requests, branches)
- Avoir un projet avec des tests automatisés
- Notions de YAML
Étape 1 sur 10 : Comprendre la CI et le modèle GitHub Actions
Chapitre 1
Comprendre la CI et le modèle GitHub Actions
L'intégration continue automatise build, tests et qualité à chaque push, pour détecter les régressions tôt et garder main toujours déployable.
L'intégration continue (CI) répond à un problème vieux comme le travail en équipe : quand plusieurs personnes modifient le même code, comment s'assurer que l'ensemble reste cohérent ? La réponse est d'automatiser, à chaque push, le build, les tests et les contrôles de qualité. Ainsi, une régression est détectée en quelques minutes par une machine, et non par un·e utilisateur·ice en production trois semaines plus tard. L'objectif est de garder la branche main toujours dans un état déployable.
Trois sigles proches sont souvent confondus, autant les clarifier une fois pour toutes. La CI vérifie que le code s'intègre proprement (tests, lint). Le CD (Continuous Delivery) prépare des artefacts prêts à déployer, le déploiement final restant déclenché à la main. Le Continuous Deployment va plus loin : dès que les tests passent, le code part automatiquement en production. Point important pour les débutant·es : tu peux — et tu devrais — avoir une CI solide sans aucun déploiement automatique. Le déploiement est une étape séparée et optionnelle.
GitHub Actions, l'outil de ce cours, s'organise autour de cinq notions qu'il faut poser d'emblée. Un workflow est un fichier YAML rangé dans .github/workflows/. Un événement le déclenche (un push, une pull request, une planification). Un job est un ensemble d'étapes qui s'exécutent sur une machine. Une étape (step) est une commande ou une action réutilisable. Et un runner est la machine virtuelle qui exécute tout ça (presque toujours ubuntu-latest). Un dernier point concret : sur un dépôt privé, les minutes d'exécution sont comptées et facturées au-delà d'un quota — optimiser sa CI a donc un impact direct sur le confort et sur la facture.
CI, CD, Continuous Deployment : ne pas confondre
- CI (Continuous Integration) : tests automatiques à chaque push, on s'assure que le code intègre proprement
- CD (Continuous Delivery) : on prépare des artefacts prêts à déployer, mais le déploiement reste manuel
- Continuous Deployment : on pousse automatiquement en prod dès que les tests passent
Anatomie d'un workflow GitHub Actions
- Workflow : un fichier YAML dans
.github/workflows/ - Event : ce qui déclenche le workflow (
push,pull_request,schedule...) - Job : un ensemble d'étapes qui tournent sur un runner
- Step : une action ou une commande shell
- Runner : la machine virtuelle qui exécute le job (
ubuntu-latestdans 95 % des cas)
name: CI
on:
push:
branches: [main]
pull_request:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: echo "Hello CI"Plan gratuit
Vrai ou faux ?
Tu peux avoir une CI solide (build, tests, qualité à chaque push) sans aucun déploiement automatique : le déploiement relève du CD, qui est une étape distincte.
Chapitre 2
Structurer ton premier workflow
Avant d'ajouter de la complexité, on cale les fondations : où placer le fichier, comment le déclencher, comment le rendre lisible.
Avant d'empiler les fonctionnalités, on pose des fondations propres. La convention est stricte et non négociable : tous les workflows vivent dans le dossier .github/workflows/, à raison d'un fichier YAML par workflow. Nomme-les explicitement selon leur rôle — ci.yml, deploy-staging.yml, release.yml — pour qu'on comprenne d'un coup d'œil ce que chacun fait. Un dépôt bien tenu a des noms de workflows qui se lisent comme une table des matières.
Le cœur de la configuration, c'est le déclencheur (la clé on), qui décide quand le workflow s'exécute. Les plus courants : push (sur un événement de poussée, éventuellement filtré par branche), pull_request (à chaque proposition de fusion), workflow_dispatch (un bouton manuel dans l'interface, pratique pour les releases) et schedule (une exécution planifiée façon cron, par exemple un audit chaque lundi). Tu peux même limiter le déclenchement à certains fichiers avec paths, pour ne pas relancer toute la CI quand seule la documentation change. Bien choisir ses déclencheurs, c'est déjà économiser des minutes et du bruit.
Deux mécanismes rendent tes workflows flexibles. Les variables d'environnement (env) se définissent à trois niveaux — workflow, job ou étape — les niveaux inférieurs surchargeant les supérieurs, exactement comme des variables locales masquent des globales. Et les conditions (if) permettent d'exécuter une étape seulement dans certains cas : if: github.ref == 'refs/heads/main' pour ne déployer que depuis la branche principale, ou if: success() pour n'agir que si les étapes précédentes ont réussi. Ces expressions transforment un script linéaire en un workflow intelligent qui s'adapte au contexte.
Emplacement et nommage
Tous les workflows vivent dans .github/workflows/. Un fichier YAML par workflow. Nomme-les explicitement : ci.yml, deploy-staging.yml, release.yml.
Les déclencheurs courants
on:
push:
branches: [main] # uniquement sur main
pull_request:
paths: # uniquement si ces fichiers changent
- 'src/**'
- 'package.json'
workflow_dispatch: # bouton manuel dans l'UI GitHub
schedule:
- cron: '0 4 * * 1' # tous les lundis à 4h UTCVariables d'environnement
Tu peux définir des env à trois niveaux : workflow, job ou step. Les niveaux inférieurs surchargent les supérieurs.
env:
NODE_ENV: test
jobs:
build:
env:
LOG_LEVEL: debug
steps:
- run: echo "NODE_ENV=$NODE_ENV LOG_LEVEL=$LOG_LEVEL"Conditions et expressions
# Step qui ne tourne que sur main
- name: Deploy
if: github.ref == 'refs/heads/main'
run: ./deploy.sh
# Step qui ne tourne que si le précédent a réussi
- if: success()
run: echo "OK"Chapitre 3
Installer Node, cacher, matricer
Le combo gagnant pour une CI Node rapide : setup-node avec cache intégré, plus une matrice pour tester plusieurs versions en parallèle.
Une CI JavaScript efficace repose sur un combo simple : l'action officielle setup-node, qui installe Node et gère le cache des dépendances en une seule déclaration. Le cache est décisif : sans lui, chaque exécution retélécharge tout node_modules, ce qui peut représenter la moitié du temps total. En ajoutant cache: 'npm', GitHub conserve le cache npm entre les runs et ne réinstalle que ce qui a changé. Bonne pratique associée : lire la version de Node depuis un fichier .nvmrc plutôt que de la coder en dur, pour garder ta CI alignée avec ton environnement local.
Un réflexe spécifique à la CI mérite d'être souligné : utilise toujours npm ci, jamais npm install. La commande npm ci installe exactement ce que décrit le package-lock.json, est plus rapide, et échoue franchement si le lock est désynchronisé du package.json. À l'inverse, npm install peut modifier subrepticement le lock et masquer un problème de dépendances. En intégration continue, on veut de la reproductibilité totale : le même code doit toujours produire la même installation.
La matrice (strategy.matrix) est l'outil qui démultiplie ta couverture sans dupliquer ton workflow. En déclarant par exemple node: [20, 22] et os: [ubuntu, macos], GitHub génère automatiquement quatre jobs en parallèle, un par combinaison. Tu valides ainsi ton code sur toutes les versions et systèmes visés en une seule passe. L'option fail-fast: false empêche l'arrêt de tous les jobs au premier échec, ce qui est précieux pour diagnostiquer : tu vois d'un coup quelles combinaisons cassent. C'est d'ailleurs ce genre de matrice qui attrape les bugs propres à un environnement — comme la différence de sensibilité à la casse des noms de fichiers entre macOS et Linux, que le scénario ci-dessous te fait justement débusquer.
setup-node avec cache automatique
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc' # lit la version depuis .nvmrc
cache: 'npm' # cache automatique de ~/.npm
- run: npm ci
- run: npm run lint
- run: npm test -- --coveragenpm ci, pas npm install
npm ci : c'est plus rapide, ça installe exactement ce qui est dans package-lock.json, et ça échoue si le lock est désynchronisé. npm install peut subtilement modifier le lock et masquer des bugs.Matrice : tester sur plusieurs versions en parallèle
jobs:
test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
node: [20, 22]
os: [ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
cache: 'npm'
- run: npm ci
- run: npm testIci, 4 jobs tournent en parallèle (2 versions Node × 2 OS). fail-fast: false évite que tout s'arrête au premier échec, ce qui aide à débugger.
🧭 Enquête : « ça passe en local, mais la CI échoue »
Ta suite de tests est verte sur ta machine. Tu pushes, et la CI casse sur le runner GitHub. Tu remontes la piste.
Le job échoue sur un test qui dépend d'un fichier. En local, tout passe. Par où commences-tu ?
Chapitre 4
Qualité, sécurité et artefacts
Une CI utile ne fait pas que passer les tests : elle attrape les régressions de qualité et conserve les preuves.
Une CI qui se contente de « faire passer les tests » n'exploite qu'une partie de son potentiel. Une bonne CI attrape aussi les régressions de qualité et conserve des preuves de ce qu'elle a produit. Le premier outil pour ça, ce sont les artefacts : des fichiers générés pendant un run (rapport de couverture, capture de test e2e, build) qu'on téléverse pour les rendre téléchargeables depuis l'interface, avec une durée de rétention configurable. Le jour où un test échoue en CI sans que tu reproduises le problème en local, un artefact de trace ou de couverture vaut de l'or.
Au-delà des tests, une CI mature enchaîne plusieurs contrôles de qualité : le lint (ESLint ou Biome) pour les mauvaises pratiques, le typecheck (tsc --noEmit) pour les erreurs de types, et éventuellement une analyse de sécurité statique comme CodeQL, gratuite sur les dépôts publics, qui détecte des failles directement dans ton code. Chacun de ces contrôles est une étape supplémentaire qui, si elle échoue, bloque la fusion. L'idée est de faire de la CI un filet à mailles multiples : ce qui passe entre un contrôle est rattrapé par le suivant.
Dernier axe, la sécurité des dépendances, à automatiser directement dans le workflow. Un npm audit --audit-level=high fait échouer la CI si une vulnérabilité grave est détectée dans tes paquets, et l'action dependency-review signale les dépendances risquées ajoutées dans une pull request avant qu'elle soit fusionnée. Complète le tout en activant Dependabot sur ton dépôt : il ouvre automatiquement des pull requests de mise à jour, que ta CI valide toute seule. L'ensemble forme une chaîne où la sécurité n'est plus une tâche qu'on oublie, mais un réflexe intégré à chaque contribution.
Upload d'artefacts (couverture, rapports)
- run: npm test -- --coverage
- uses: actions/upload-artifact@v4
with:
name: coverage
path: coverage/
retention-days: 7L'artefact est téléchargeable depuis l'UI du run. Tu peux aussi le récupérer dans un autre job avec actions/download-artifact@v4.
Lint, typecheck, analyse statique
npm run lint: ESLint, Biome ou ton outil habituelnpm run typecheck:tsc --noEmitpour TypeScript- CodeQL : analyse de sécurité automatique, gratuite sur les repos publics, via
github/codeql-action@v3
Sécurité des dépendances
# Audit npm bloquant sur les vulnérabilités hautes
- run: npm audit --audit-level=high
# Review automatique des dépendances ajoutées dans une PR
- uses: actions/dependency-review-action@v4
if: github.event_name == 'pull_request'Dependabot
Chapitre 5
Secrets, permissions et sécurité
Une CI mal configurée est une porte ouverte. Quelques réflexes pour limiter les dégâts en cas de compromission.
Une CI a besoin d'accéder à des ressources protégées — publier un paquet, déployer, appeler une API — et manipule donc des secrets. Or une CI mal configurée est une porte grande ouverte : elle s'exécute automatiquement, souvent avec des droits étendus, et exécute du code parfois écrit par des tiers. Ce chapitre pose les réflexes qui limitent les dégâts en cas de compromission. GitHub propose plusieurs portées de secrets : au niveau du dépôt, au niveau d'un environnement (avec possibilité d'exiger une approbation), ou au niveau de l'organisation pour les partager entre projets.
Un piège subtil mérite toute ton attention : GitHub masque un secret dans les logs uniquement si sa valeur exacte y apparaît. Dès que tu le transformes — un encodage base64, une découpe, une concaténation — la version transformée n'est plus reconnue et peut s'afficher en clair. La règle absolue en découle : aucun secret ne doit jamais transiter par la sortie standard. On ne fait jamais echo d'un token, même « pour débugger ». Cette seule discipline évite la majorité des fuites accidentelles.
Trois pratiques renforcent la sécurité en profondeur. Le principe du moindre privilège : le jeton automatique GITHUB_TOKEN a beaucoup de droits par défaut, alors on les réduit explicitement au strict nécessaire avec un bloc permissions (tout ce qui n'est pas listé passe à none). L'épinglage des actions tierces par SHA : un tag comme @v4 peut être déplacé, voire détourné par un mainteneur compromis, alors qu'une empreinte SHA est immuable et te garantit d'exécuter exactement la version que tu as auditée. Et l'OIDC pour les clouds : plutôt que de stocker une clé AWS ou GCP permanente, tu laisses GitHub émettre un jeton court et signé que ton cloud accepte le temps d'un run — plus aucune clé longue durée à faire fuiter.
Secrets : où, comment
- Repository secrets : disponibles dans tous les workflows du dépôt
- Environment secrets : scopés à un environnement (
production,staging), peuvent exiger une approbation - Organization secrets : partagés sur plusieurs dépôts d'une organisation
- run: ./deploy.sh
env:
API_TOKEN: ${{ secrets.API_TOKEN }}Ne jamais echo un secret
echo $API_TOKEN | base64, le résultat sera visible en clair. Réflexe : aucun secret ne doit transiter par stdout.Le principe du moindre privilège
Le token GITHUB_TOKEN a par défaut beaucoup de droits. Réduis-les explicitement au strict nécessaire :
permissions:
contents: read # lecture du code, c'est tout
pull-requests: write # commenter les PR
# tous les autres scopes par défaut à 'none'
jobs:
test:
runs-on: ubuntu-latest
# ...Pinner les actions tierces par SHA
Un tag (@v4) peut être déplacé par le mainteneur, voire compromis. Un SHA est immuable. Pour les actions tierces, pinne par SHA.
# Action officielle GitHub : tag OK
- uses: actions/checkout@v4
# Action tierce : pinne le SHA
- uses: super/sketchy-action@e2f4a3b1c9d8... # v2.1.0OIDC pour les clouds : pas de secret long terme
Pour t'authentifier à AWS, GCP ou Azure, n'utilise plus de clés stockées en secret. GitHub peut émettre un jeton OIDC court signé que ton cloud accepte. Plus de fuite possible.
permissions:
id-token: write # nécessaire pour OIDC
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123:role/github-deploy
aws-region: eu-west-3Vrai ou faux ?
Un secret stocké dans GitHub Actions est automatiquement masqué partout, même si je le décode ou le transforme dans un step.
Chapitre 6
Performance et parallélisme
Une CI lente, on l'évite. Voilà comment passer de 12 minutes à 3 minutes sur un projet de taille moyenne.
Une CI lente est une CI qu'on finit par contourner : on merge sans attendre, on désactive des étapes, on perd le bénéfice de l'automatisation. Garder une CI rapide est donc un enjeu de qualité autant que de confort — et sur un dépôt privé, un enjeu de coût, puisque les minutes sont facturées. La bonne nouvelle, c'est que quelques réglages font souvent passer une CI de douze minutes à trois, sans rien sacrifier.
Le premier levier est la concurrency. Sans elle, pousser trois commits coup sur coup lance trois builds complets, dont les deux premiers sont déjà obsolètes avant même de finir. En déclarant un groupe de concurrency avec cancel-in-progress: true, chaque nouveau run annule automatiquement les précédents de la même branche : tu ne paies que pour le dernier état du code, et tu obtiens ton retour plus vite. Le deuxième levier est le parallélisme : plutôt qu'un job monolithique, on découpe en jobs (installation, lint, tests) reliés par needs, ce qui laisse lint et tests s'exécuter en même temps une fois l'installation faite.
Le troisième levier est le cache, sous ses deux formes. Le cache de dépendances de setup-node, déjà vu, évite de retélécharger les paquets. Un cache personnalisé (actions/cache) va plus loin en conservant des résultats de build intermédiaires (le cache de Next.js, par exemple) d'un run à l'autre, avec une clé calculée sur le contenu de tes fichiers de lock. Enfin, si plusieurs dépôts partagent la même CI, factorise-la dans un workflow réutilisable (on: workflow_call) que les autres appellent : tu maintiens une seule source de vérité au lieu de copier-coller la même configuration partout. Optimiser sa CI, c'est appliquer ces leviers dans l'ordre jusqu'à retrouver un retour quasi instantané.
Concurrency : annuler les runs obsolètes
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: trueSi tu pushes 3 commits coup sur coup, sans concurrency tu lances 3 builds. Avec cancel-in-progress: true, les anciens sont annulés dès qu'un nouveau démarre. Moins de minutes brûlées, feedback plus rapide.
Chaîner et paralléliser les jobs
jobs:
install:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 22, cache: 'npm' }
- run: npm ci
lint:
needs: install
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 22, cache: 'npm' }
- run: npm ci
- run: npm run lint
test:
needs: install
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 22, cache: 'npm' }
- run: npm ci
- run: npm testneeds: définit les dépendances. Ici, lint et test tournent en parallèle après install. Le cache npm évite de re-télécharger les dépendances dans chaque job.
Cache personnalisé
- uses: actions/cache@v4
with:
path: |
.next/cache
node_modules/.cache
key: ${{ runner.os }}-build-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-build-Workflows réutilisables
on: workflow_call. Les autres workflows l'appellent avec uses: org/repo/.github/workflows/ci.yml@main.Chapitre 7
Du CI au CD : déployer en confiance
Une fois les tests verts, le même workflow peut publier ou déployer. La clé : les environments GitHub pour cadrer ça proprement.
Une fois tes tests verts et ta CI solide, la suite naturelle est le déploiement : le même workflow qui vérifie ton code peut aussi le publier. Mais déployer touche à la production, là où une erreur a des conséquences réelles — d'où le besoin d'un cadre. Sur GitHub, ce cadre s'appelle les environments : des cibles nommées (staging, production) auxquelles on attache des règles de protection. Tu peux exiger une approbation manuelle avant un déploiement, restreindre les branches autorisées, et stocker des secrets spécifiques à chaque environnement.
Le motif classique combine plusieurs des notions déjà vues. Un job deploy déclaré avec needs: test ne démarre qu'une fois les tests réussis ; une condition if: github.ref == 'refs/heads/main' garantit qu'on ne déploie que depuis la branche principale ; et la clé environment rattache le job à l'environnement production, avec ses protections et son URL. Cet enchaînement — tester, puis conditionner, puis déployer dans un environnement protégé — est le squelette d'un pipeline de livraison sûr, que tu retrouveras sur la plupart des projets sérieux.
En pratique, tu n'as pas toujours à tout écrire toi-même. Pour un site statique ou une application front (Next.js, Astro, Vite), le plus simple reste de connecter le dépôt à Vercel ou Netlify, qui gèrent build et déploiement à chaque push — ta CI GitHub se limite alors au job de test. Pour publier un paquet npm, on déclenche plutôt le workflow sur les tags de version. Et pour le cas où un déploiement casse la production, GitHub offre un rollback d'une simplicité rassurante : ouvre le dernier run qui fonctionnait dans l'onglet Actions et clique sur Re-run all jobs. Tu reviens à la version saine en un clic, sans toucher au code — exactement l'esprit d'un pipeline dans lequel on peut avoir confiance.
Environments avec règles de protection
Crée des environments dans Settings → Environments : staging, production. Tu peux exiger une approbation manuelle, limiter aux branches autorisées, et stocker des secrets dédiés.
jobs:
test:
runs-on: ubuntu-latest
steps: # ...
deploy:
needs: test
if: github.ref == 'refs/heads/main'
environment:
name: production
url: https://anais-formation-tech.fr
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: ./deploy.sh
env:
DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}Déploiement statique : Vercel, Netlify, Pages
Pour un site Next.js, Astro ou Vite, le plus simple est de connecter le dépôt à Vercel ou Netlify, qui s'occupent du build + déploiement à chaque push. Tu n'as alors plus qu'un job test en GitHub Actions, le déploiement est géré ailleurs.
Publier un package npm
on:
push:
tags: ['v*']
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
registry-url: 'https://registry.npmjs.org'
- run: npm ci
- run: npm test
- run: npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}Rollback simple
Pour aller plus loin
- Documentation GitHub Actions (en français) · docs.github.com (nouvel onglet)
- Syntaxe des workflows pour GitHub Actions · docs.github.com (nouvel onglet)
- Mettre en cache les dépendances pour accélérer les workflows · docs.github.com (nouvel onglet)
- GitHub Marketplace : actions réutilisables · github.com (en anglais) (nouvel onglet)
🛠️ Exercice optionnel
Mettre en place une CI complète sur un projet Node
Tu as un projet Node.js avec des tests existants (n'importe quel projet perso fera l'affaire). Tu vas construire une CI propre, performante et sécurisée.
Ta mission
- Crée
.github/workflows/ci.ymlqui se déclenche surpushetpull_requestversmain. - Job
testavec :- matrice Node 20 et 22
- cache npm via
actions/setup-node@v4 - lint, typecheck et tests avec couverture
- upload de la couverture en artefact (rétention 7 jours)
- Job
auditparallèle exécutantnpm audit --audit-level=high. - Active
concurrencypour annuler les runs obsolètes sur la même branche. - Réduit les
permissionsàcontents: readuniquement. - Sur GitHub : protège
mainen exigeant quetestpasse avant un merge.
Livrable : la PR contenant le workflow, une capture d'un run vert, et un badge de build dans le README.
Tu bloques ? Des indices, à dévoiler quand tu en as besoin.
Indice 1
Indice masqué.
Indice 2
Indice masqué.
Indice 3
Indice masqué.
✅ QCM de fin de cours
Teste tes acquis
10 questions, plusieurs réponses parfois possibles. Coche tout ce qui te semble juste, puis valide pour voir ton score et les explications.
- 1
Quelle version de
actions/checkoutest recommandée en 2025 ? - 2
À quoi sert
concurrency.cancel-in-progress: true? - 3
Pourquoi préférer
npm ciànpm installen CI ? - 4
Comment limiter les permissions du
GITHUB_TOKEN? - 5
Quelle clé permet de déclencher manuellement un workflow depuis l'interface GitHub ?
- 6
Que fait
strategy.matrix? - 7
Quel mécanisme permet de s'authentifier vers AWS depuis GitHub Actions sans stocker de clé long terme ?
- 8
Différence entre
secretsetvars? - 9
Pourquoi pinner une action tierce par SHA plutôt que par tag ?
- 10
Où sont stockés les artefacts uploadés par
actions/upload-artifact@v4?
Tu peux laisser des questions sans réponse, elles compteront comme fausses.
🎓 Attestation
Ton attestation de réussite
Termine le QCM avec au moins 80% de bonnes réponses pour débloquer ton attestation de réussite.
Tu veux ce cours pour ton équipe ?
Je peux adapter et animer ce cours pour tes formateur·ices ou tes apprenant·es, en présentiel ou en distanciel. Parlons-en pendant l'audit gratuit.
Réserver un audit gratuit →