Dix secondes. C'est ce que coûte l'approbation d'un bump patch : ouvrir la PR, jeter un œil au numéro de version, cliquer. Sur la dizaine de dépôts open source que je maintiens, avec plusieurs mises à jour par semaine sur chacun, ces dix secondes deviennent un flux continu de clics à faible valeur ajoutée. Voici comment j'ai standardisé la configuration Dependabot et branché un auto-merge conditionnel dessus, pour absorber ce bruit sans perdre le contrôle sur ce qui compte vraiment.
Le problème : Dependabot qui spamme
Le scénario est toujours le même : build(deps): bump X from 1.2.3 to 1.2.4, une PR, une notification, une revue. Puis une autre, pour une dépendance différente, sur un autre dépôt, le lendemain. Le contenu de la revue tient rarement en plus d'un coup d'œil au changelog, mais le geste, lui, se répète.
Le vrai coût n'est pas le temps passé sur chaque PR individuellement, il est ailleurs :
- Le bruit dans la liste des PRs et les notifications, qui noie dans le même flot les patchs anodins et les mises à jour majeures, les seules à porter un vrai risque de breaking change
- Une revue qui devient un réflexe plutôt qu'un jugement, avec le risque de cliquer « merge » sans vraiment regarder
Sur un seul dépôt, ce bruit reste gérable. À l'échelle de plusieurs dépôts personnels maintenus en parallèle, il devient un vrai coût d'attention.
Toute la suite repose sur une distinction : celle que SemVer pose entre un bump major, seul autorisé à casser l'API publique, et les bumps minor et patch, censés rester compatibles. C'est cette frontière qui va servir de ligne de partage entre ce qu'on automatise et ce qu'on continue de lire.
Le prérequis non négociable : des tests qui détectent les vrais problèmes
Avant d'aller plus loin, le point sur lequel je ne transige pas : cet auto-merge revient à laisser GitHub fusionner une PR tout seul, dès que les checks passent au vert et sans qu'un humain la relise. Il ne vaut donc que ce que vaut cette CI. Si elle se limite à un build ou à un lint, « CI verte » ne veut rien dire de plus que « ça compile ». Automatiser le merge dans ce contexte ne fait pas gagner du temps sur une tâche sans risque, ça fait juste merger à l'aveugle plus vite. C'est le raisonnement du harness engineering appliqué aux dépendances : la confiance ne vient pas de la revue qu'on retire, elle vient du harnais qui la remplace.
Ce pattern est une récompense pour qui a déjà une suite de tests fiable, pas un moyen de s'en passer. Il faut donc :
- Une suite de tests qui couvre les chemins critiques du projet, pas seulement « le programme démarre sans planter ». Si le vôtre n'en est pas là, les tests par propriété sont une piste pour couvrir des règles métier sans écrire un cas par scénario
- Une CI qui exécute réellement ces tests sur les PRs Dependabot, au même titre que sur les PRs humaines
- Un check marqué comme obligatoire via la protection de branche, pour que l'auto-merge attende un vrai signal plutôt qu'un job optionnel que personne ne regarde
Le dernier point est une contrainte technique autant qu'une bonne pratique : sans check requis, une PR Dependabot est mergeable dès son ouverture, et la commande d'auto-merge vue plus bas échoue au lieu de programmer quoi que ce soit.
Sur un dépôt sans couverture de tests fiable, je n'active pas ce pattern et je ne le recommanderais à personne. La suite part du principe que ce prérequis est rempli.
Le pattern en trois leviers
La configuration que j'ai standardisée sur mes dépôts tient dans .github/dependabot.yml, à la racine du dépôt et sur la branche par défaut. Ailleurs, Dependabot ne la lit pas :
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
cooldown:
default-days: 5
commit-message:
prefix: "build"
include: "scope"
groups:
dependencies:
patterns:
- "*"
update-types:
- "minor"
- "patch"
Deux clés relèvent plutôt de la configuration de base que du réglage du volume de PRs. directory indique où chercher le manifeste (package.json, Cargo.toml…), ici la racine. commit-message est ce qui produit le build(deps): bump X… du début de l'article : prefix: "build" fixe le type de commit conventionnel, include: "scope" ajoute le (deps). Pratique quand un changelog est généré à partir des commits, mais sans effet sur le volume de PRs.
Les leviers qui comptent, eux, sont ailleurs.
Groups : une PR au lieu de dix
La clé groups regroupe toutes les mises à jour minor et patch dans une seule PR nommée dependencies, au lieu d'une PR par dépendance. Si je ne devais garder qu'un seul de ces réglages, ce serait celui-là : c'est le seul qui attaque le volume, et dix bumps patch dans la même semaine deviennent une seule revue.
Espacer le schedule.interval ne remplace pas ce levier. Sans groups, passer de weekly à monthly laisse chaque dépendance ouvrir sa propre PR, simplement moins souvent. Le nombre de PRs à merger ne diminue pas, il s'accumule plus longtemps avant d'arriver d'un coup, avec des mises à jour plus anciennes et donc plus éloignées du code en place. Les mises à jour de sécurité, elles, échappent à ce délai : Dependabot les ouvre immédiatement.
Un détail à connaître avant de laisser tourner : Dependabot plafonne par défaut à 5 PRs de mise à jour ouvertes simultanément par écosystème (open-pull-requests-limit). Avec ce pattern, les majors sortent en PRs individuelles ; si vous en laissez traîner cinq sans les traiter, la PR groupée ne s'ouvre plus du tout, silencieusement. C'est une bonne incitation à vider la file des majors, mais mieux vaut savoir que le blocage vient de là.
Cooldown : laisser une version faire ses preuves
cooldown.default-days: 5 fait attendre Dependabot cinq jours après la sortie d'une nouvelle version avant de la proposer. La plupart des régressions introduites par une release sont signalées par la communauté dans les premiers jours ; il s'agit de ne pas être la personne qui les découvre.
À noter : ce réglage ne crée pas ce délai, il le durcit. Dependabot applique désormais un cooldown de 3 jours par défaut aux mises à jour de version, sans rien configurer. Passer à 5 jours est donc un ajustement, pas un changement de nature, et je ne le défendrais pas sur un dépôt suivi au quotidien. Sur ceux que je n'ouvre qu'une fois par mois, en revanche, ces deux jours de plus valent leur prix.
Ce délai a un revers : il retarde de la même façon un correctif que vous attendez activement. Une version qui corrige un bug qui vous bloque met, elle aussi, ces quelques jours à vous parvenir. Sur un projet où un fix précis est urgent, rien n'empêche de forcer manuellement la mise à jour en attendant que Dependabot la propose de lui-même.
En revanche, la crainte la plus naturelle (« est-ce que je viens de retarder de cinq jours un correctif de faille ? ») n'a pas lieu d'être : le cooldown ne s'applique qu'aux mises à jour de version. Les mises à jour de sécurité déclenchées par une alerte Dependabot y échappent complètement, exactement comme elles échappent au schedule.interval.
Le tri implicite entre minor/patch et major
Le champ update-types du groupe ne retient que minor et patch. Les mises à jour majeures restent en dehors du groupe : chacune ouvre sa propre PR, individuelle, qui ne sera jamais auto-mergée. C'est ce tri qui permet d'automatiser sans perdre le contrôle sur les changements qui portent un vrai risque de rupture.
L'auto-merge conditionnel
La configuration Dependabot regroupe et espace les mises à jour, mais ne les merge pas. Ce rôle revient à un workflow GitHub Actions, .github/workflows/dependabot-auto-merge.yml, lui aussi à placer sur la branche par défaut :

name: Dependabot auto-merge
on: pull_request
permissions:
contents: write
pull-requests: write
jobs:
auto-merge:
if: github.event.pull_request.user.login == 'dependabot[bot]'
runs-on: ubuntu-latest
steps:
- name: Dependabot metadata
id: metadata
uses: dependabot/fetch-metadata@25dd0e34f4fe68f24cc83900b1fe3fe149efef98 # v3.1.0
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Enable auto-merge for minor/patch updates
if: steps.metadata.outputs.update-type != 'version-update:semver-major'
run: gh pr merge --auto --squash "$PR_URL"
env:
PR_URL: ${{ github.event.pull_request.html_url }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Ce workflow reprend la structure de l'exemple officiel GitHub, avec deux écarts. La condition est une liste noire (!= semver-major) là où la doc propose une liste blanche, dépendance par dépendance. J'assume cet élargissement, mais uniquement parce que le prérequis sur les tests est tenu : sans lui, la liste blanche de la doc reste le bon choix. Et surtout, il ne vaut qu'adossé à la configuration dependabot.yml décrite plus haut. La doc ne traite que la moitié Actions du problème, celle du merge, jamais celle du volume de PRs.
Quatre choses à noter :
- Le job ne s'exécute que sur les PRs ouvertes par
dependabot[bot]: aucune PR humaine ne passe par ce chemin. Le test porte sur l'auteur de la PR (github.event.pull_request.user.login) et non surgithub.actor, qui devient le login de la personne ayant relancé le workflow lors d'un re-run manuel - Le bloc
permissionsn'est pas décoratif. GitHub traite les workflows déclenchés par Dependabot comme s'ils venaient d'un fork : leGITHUB_TOKENy est en lecture seule par défaut. Sans cette élévation explicite, le workflow n'a pas le droit d'activer l'auto-merge - L'action
dependabot/fetch-metadataexpose le type de mise à jour sous la formeversion-update:semver-major,version-update:semver-minorouversion-update:semver-patch, ce qui évite de parser le titre de la PR. Sur une PR groupée, elle retourne le bump le plus élevé du lot, ce qui rend le test ci-dessus correct malgré le regroupement gh pr merge --auton'effectue pas un merge immédiat : il active l'auto-merge natif de GitHub, qui attend que les checks requis passent avant d'agir. Le workflow ne court-circuite donc jamais la CI
Le filtre sur update-type fait le reste : dès qu'une mise à jour est majeure, l'étape est sautée et la PR attend une revue humaine comme n'importe quelle autre.
Les réglages GitHub à vérifier avant de copier ce workflow
Ce workflow ne fonctionne que si le dépôt est configuré pour l'accepter. Les réglages concernés sont dans les paramètres du dépôt, et les manquer fait échouer gh pr merge --auto ou le laisse sans effet :
- Allow auto-merge doit être activé dans Settings → General → Pull Requests. Il ne l'est pas par défaut, et sans lui
gh pr merge --autoéchoue directement - Le mode de merge utilisé par le workflow (ici
--squash) doit être autorisé dans ces mêmes paramètres. Si seul le merge simple ou le rebase est permis, l'option échoue - Si la protection de branche exige des approbations, l'auto-merge ne les remplace pas : il programme le merge, qui reste bloqué tant qu'elles manquent. Dependabot ne s'auto-approuve pas, il faut donc soit exempter ses PRs, soit garder une approbation manuelle même pour les mises à jour groupées
Quand rien ne se passe : où regarder
Le mode d'échec le plus déroutant de ce pattern, c'est le silence. Les endroits à consulter, dans l'ordre :
- Insights → Dependency graph → Dependabot liste chaque écosystème configuré, la date du dernier check et, surtout, les erreurs de parsing du
dependabot.yml. Un fichier invalide n'y produit aucune PR et aucune notification, juste une ligne d'erreur ici - L'onglet Actions de la PR concernée : si le job
auto-mergen'apparaît pas du tout, c'est la conditionifdu job qui a filtré ; s'il apparaît en échec, le message deghest explicite - La PR elle-même, qui affiche un bandeau Auto-merge enabled quand la programmation a réussi. En son absence, le merge n'est pas planifié, même si la CI passe au vert ensuite
Le message le plus fréquent à ce stade est un Pull request is in clean status retourné par gh : la PR est déjà mergeable, il n'y a rien à programmer. Autrement dit, aucun check n'est réellement requis sur la branche, ce qui ramène au prérequis du début. Une fois la configuration corrigée, les commentaires @dependabot rebase et @dependabot recreate relancent le cycle depuis la PR.
Pourquoi le risque reste maîtrisé
Une fois le prérequis sur les tests posé, le dispositif repose sur des garde-fous qui se cumulent :
- L'auto-merge attend que les checks CI passent, il ne les remplace pas
- Le cooldown limite le risque de récupérer une version fraîchement publiée et pas encore éprouvée par la communauté
- Les mises à jour majeures, qui portent le vrai risque de rupture, ne sont jamais auto-mergées : elles restent sous revue manuelle
S'y ajoute une précaution qui relève de la chaîne d'approvisionnement : fetch-metadata est épinglée sur son SHA de commit (@25dd0e3..., avec # v3.1.0 en commentaire pour la lisibilité), pas sur un tag mutable. Un tag comme v3 peut être déplacé sans prévenir ; un SHA, non. Sur un workflow qui tourne tout seul avec la permission contents: write, je considère ça comme non négociable.
Reste que ces garde-fous couvrent la sécurité et le versionnage, pas le comportement du code. Un bump patch qui casse silencieusement une fonctionnalité passera tous ces filtres sans encombre : seule une suite de tests capable de le détecter l'arrêtera. Et une suite peut très bien être verte sans rien vérifier, comme le rappellent les tests qui se mockent eux-mêmes repérés en revue de PRs, un travers du même ordre que ceux décrits dans notre méthode pour gérer 30 PRs par semaine.
Retour d'expérience : déployer le pattern sur plusieurs dépôts
J'ai appliqué cette configuration sur tous mes dépôts open source personnels, sans distinction de langage ni d'écosystème : drawio-export suit une image Docker et ses actions, agent-skills ne surveille que ses GitHub Actions. Une série de commits reproduisant le même changement (build: standardize dependabot config and enable auto-merge for minor/patch updates) a suffi. Sa brièveté a beaucoup aidé : pas besoin de la repenser dépôt par dépôt.
En pratique, seules les deux premières lignes du bloc bougent d'un dépôt à l'autre :
- package-ecosystem: "cargo" # ou "gomod", "github-actions", "gitsubmodule"…
directory: "/"
Le reste du bloc est identique partout, et cooldown.default-days est supporté par tous les écosystèmes que Dependabot gère. Il existe bien des réglages plus fins par type de version, semver-major-days, semver-minor-days et semver-patch-days, mais ils ne sont disponibles que sur une partie d'entre eux : github-actions, docker ou terraform n'en bénéficient pas. default-days seul suffit. Le workflow d'auto-merge, lui, ne regarde que le type de mise à jour, donc il se copie sans la moindre adaptation.
La seule variation que je me suis autorisée concerne github-slug-action (5 ans de maintenance racontés ici), qui expose ses propres actions : j'y ai ajouté un second bloc github-actions ciblant mes actions internes, pour les traiter séparément du reste.
L'effet le plus net porte sur le volume, pas sur le temps gagné PR par PR : les mises à jour mineures et patch, l'essentiel du flot, ne demandent quasiment plus d'intervention. Ce qui reste dans ma liste de revues, ce sont les majeures, les seules qui méritent une lecture attentive.
Conclusion
La configuration tient en quelques lignes et se copie d'un dépôt à l'autre. C'est le morceau facile. Rien là-dedans ne remplace une suite de tests qui détecte réellement les problèmes : c'est elle qui décide si chaque merge automatique est un gain de temps ou un pari. Automatiser Dependabot déplace le travail, de la revue des PRs vers l'entretien des tests.
Pour aller plus loin
- Référence des options
dependabot.yml:cooldown,groups,open-pull-requests-limitet le support par écosystème - Automatiser Dependabot avec GitHub Actions : le tutoriel officiel dont ce workflow reprend la structure
dependabot/fetch-metadata: la liste complète des sorties exposées par l'action- Dépanner Dependabot sur GitHub Actions : le
GITHUB_TOKENen lecture seule et les autres pièges de permissions