Renovate est un outil permettant de mettre à jour des versions de dépendances. Il est disponible en open-source sur GitHub et est géré par la société mend.io.
Renovate est compatible avec la majorité des plateformes de versioning de code : GitHub, GitLab, Bitbucket, Azure DevOps, AWS CodeCommit, Gitea et Forgejo.
Sur GitHub (.com et Enterprise Server), Renovate est disponible sous la forme d’une application partagée que l’on peut intégrer à son dépôt. Sinon, pour toutes les plateformes, Renovate peut être utilisé en auto-hébergement.
Son utilisation peut être découpée en trois grandes parties :
Nous allons utiliser une application d’exemple avec un frontend en React et un backend en Node.js, et une base de données MongoDB.
La première étape est de copier ces fichiers dans un projet GitLab. Dans cette application, on retrouve 3 types de dépendances : des packages NPM, des fichiers Dockerfile et un fichier Docker-Compose.
Clone de l’application de démonstration
On peut alors activer Renovate sur ce projet. Pour cela, sur GitLab, l’outil met à disposition un pipeline Renovate Runner en libre accès, qui se base sur les pipeline schedules. Il s’agit simplement d’une façon de lancer de manière régulière un pipeline sur GitLab. Il fonctionne par l’inclusion d’un pipeline via la directive include. Il suffit donc de créer un fichier .gitlab-ci.yml qui contient le code suivant (en remplaçant la version par la dernière disponible) :
# .gitlab-ci.yml
include:
- remote: https://gitlab.com/renovate-bot/renovate-runner/-/raw/v16.46.2/templates/renovate.gitlab-ci.yml
Inclusion de pipeline GitLab
Pour configurer ce pipeline, il faut définir 2 variables de CI/CD au niveau du projet GitLab :
Ensuite, il faut créer un pipeline schedule (Build > Pipeline Schedules). Pour l'exemple, nous allons l’exécuter tous les jours à midi.
Pipeline Schedule dans GitLab
Il est alors possible de forcer l’exécution du pipeline schedule depuis la page dédiée de GitLab, et on peut retrouver le résultat de l’exécution dans les logs :
INFO: Autodiscovered repositories
"repositories": ["antoine.boursin/wescale-demo-renovate"]
INFO: Repository started (repository=antoine.boursin/wescale-demo-renovate)
"renovateVersion": "37.57.2"
INFO: Branch created (repository=antoine.boursin/wescale-demo-renovate, branch=renovate/configure)
"commit": "5fe8ef91d09da95b0d08eaa423ce08ce67827f69",
"onboarding": true
INFO: Dependency extraction complete (repository=antoine.boursin/wescale-demo-renovate, baseBranch=main)
"stats": {
"managers": {
"docker-compose": {"fileCount": 1, "depCount": 1},
"dockerfile": {"fileCount": 2, "depCount": 6},
"npm": {"fileCount": 2, "depCount": 22}
},
"total": {"fileCount": 5, "depCount": 29}
}
INFO: Onboarding PR created (repository=antoine.boursin/wescale-demo-renovate)
"pr": "Pull Request #1"
INFO: Repository finished (repository=antoine.boursin/wescale-demo-renovate)
"cloned": true,
"durationMs": 16352
Logs de l’exécution de Renovate
On y constate que Renovate a détecté notre projet, les dépendances qu’il contient et qu’il a créé une Merge Request de OnBoarding pour nous aider à le configurer sur ce projet. Si on ne souhaite finalement pas activer Renovate, on peut simplement fermer cette merge request et il ne prendra plus en compte ce projet.
Merge Request de OnBoarding
Dedans, il y a un fichier de configuration basique de Renovate, qui peut être utilisé en l’état même si des améliorations sont possibles (ce que nous ferons dans la seconde moitié de l’article !).
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:recommended"
]
}
Configuration minimale proposée par Renovate
On y retrouve également la liste des dépendances détectées et les mises à jour disponibles. Après relecture, on peut merger cette Merge Request et réexécuter le pipeline schedule de Renovate. Cette fois-ci, il y a beaucoup plus d’actions dans les logs : Renovate est allé vérifier les différentes versions de nos dépendances et a créé des Merge Requests pour celles qui n’étaient pas à jour.
(nombreuses) Merge Requests ouvertes par Renovate
Renovate a également ouvert une Issue nommée Dependency Dashboard, pour nous permettre de suivre les mises à jour de nos dépendances. En cas de comportement non attendu, on peut s’y référer pour trouver une explication. C’est par exemple le cas si des Merge Requests ont été bloquées à cause d’un rate-limiting. On peut alors forcer leur exécution en cochant les cases présentes sur l’issue.
Dependency Dashboard de Renovate
On peut alors passer à une configuration plus fine de Renovate, c’est ce que nous allons voir avec les meilleures options de Renovate dans la partie suivante.
Les options présentées ci-dessous peuvent être ajoutées dans le fichier renovate.json pour améliorer son efficacité et l’adapter à nos besoins.
La première chose que l’on constate est que Renovate peut ouvrir un grand nombre de Merge Requests. Il est possible de grouper les mises à jour d’un même type dans une seule Merge Request. Pour cela, il faut utiliser le paramètre packageRules. Il permet d’appliquer des règles uniquement à certaines dépendances en fonction de leur nom ou bien du fichier dans lequel elles se trouvent. Dans l’exemple ci-dessous, on regroupe toutes les dépendances Node.js dans une seule Merge Request.
{
"packageRules": [
{
"matchManagers": ["npm"],
"groupName": "nodejs dependencies"
}
]
}
packageRules pour grouper les dépendances Node.js
Si on relance Renovate, on constate que la plupart des Merge Requests ont été fermées, et les mises à jour regroupées dans deux Merge Requests : une pour les mises à jour majeures et une pour les autres, mineures ou patch.
Il ne reste que deux Merge Requests pour les dépendances Node.js
Dans certains cas, on veut ne mettre à jour que la version de patch d’une dépendance. C’est notamment le cas si on travaille avec des versions de Kubernetes (v1.Y.Z) sans mettre à jour la version mineure (le Y). Par exemple, si on utilise la version 1.25.2, on souhaite une mise à jour vers la version 1.25.3 mais pas vers la version 1.26.1.
Pour cela, on peut de nouveau utiliser le paramètre packageRules pour modifier les actions sur les versions de Kubernetes. Dans l’exemple ci-dessous, la première règle permet de séparer les Merge Requests majeures, mineures et de patch (car par défaut les mineures et les patch sont regroupées). La seconde désactive les mises à jour majeures et mineures pour Kubernetes, pour ne garder que les patch.
{
"packageRules": [
{
"matchPackageNames": ["kubernetes/kubernetes"],
"separateMajorMinor": true,
"separateMinorPatch": true
},
{
"matchPackageNames": ["kubernetes/kubernetes"],
"matchUpdateTypes": ["major", "minor"],
"enabled": false
}
]
}
packageRules pour mettre à jour uniquement les patch
L’initiative Conventional Commits vise à encadrer les messages de commit que l’on peut pousser sur les projets Git. Cela peut donner des commits comme deps(renovate): update package to vX.Y.Z. Pour obtenir ce résultat, trois paramètres sont à activer pour configurer l’utilisation de Conventional Commits, et personnaliser le type et le scope. Leur format est un peu différent des autres car il s’agit de presets, des morceaux de configuration fournis par Renovate (comme config:recommended que nous avons utilisé précédemment).
{
"extends": [
":semanticCommits",
":semanticCommitTypeAll(deps)",
":semanticCommitScope(renovate)"
]
}
Activation des Conventional Commits avec Renovate
Il est à noter que la valeur par défaut pour le paramètre semanticCommits est auto, qui va analyser les commits précédents dans le projet et détecter s’ils utilisent les Conventional Commits ou non. Les nouveaux commits de Renovate seront adaptés en fonction de cette détection.
Parfois, les dépendances que notre application utilise sont disponibles uniquement après authentification. Il est donc nécessaire pour Renovate d’avoir des identifiants qui lui permettent d’aller vérifier les versions qui existent. Le paramètre à utiliser est hostRules, qui fonctionne de manière semblable à packageRules mais qui configure les paramètres liés à des hôtes distants.
{
"detectHostRulesFromEnv": true,
"hostRules": [
{
"hostType": "docker",
"matchHost": "registry.company.com",
"username": "serviceaccount"
}
]
}
hostRules pour accéder à une registry privée
Dans l’exemple ci-dessus, nous n’avons bien évidemment pas le mot de passe écrit en clair dans le fichier de configuration, il est donc nécessaire de le passer en tant que variable d’environnement avec un format spécifique. Pour cela, il faut activer le paramètre detectHostRulesFromEnv et créer une variable d’environnement secrète DOCKER_REGISTRY_COMPANY_COM_PASSWORD=P4S$W0RD pour le définir.
Pour les autres types de gestionnaires de package, le format de la variable d’environnement est assez proche, par exemple pour Artifactory on pourra utiliser une règle "hostType": "maven" et une variable MAVEN_ARTIFACTORY_COMPANY_COM_PASSWORD.
Parfois, certaines dépendances ne peuvent pas être détectées automatiquement, et on peut alors utiliser des expressions régulières pour aller les chercher dans les différents fichiers.
Dans l’exemple suivant, on va trouver dans le fichier Dockerfile une ligne qui commence par ENV YARN_VERSION, pour récupérer la version actuelle et proposer des mises à jour.
{
"customManagers": [
{
"customType": "regex",
"fileMatch": ["^Dockerfile$"],
"matchStrings": ["ENV YARN_VERSION=(?<currentValue>.*?)\\n"],
"depNameTemplate": "yarn",
"datasourceTemplate": "npm"
}
]
}
customManager regex
Plus d’informations sont disponibles dans la documentation officielle de Renovate : https://docs.renovatebot.com/modules/manager/regex/
En plus des dépendances applicatives, Renovate peut se mettre à jour lui-même ! Dans notre cas, on utilise un pipeline GitLab avec une version fixée qui peut être détectée avec le manager regex de la section précédente. Ensuite, Renovate ouvrira une Merge Request lorsque des nouvelles versions seront disponibles.
{
"customManagers": [
{
"customType": "regex",
"fileMatch": ["^\\.gitlab-ci\\.yml$"],
"matchStrings": ["https://gitlab\\.com/renovate-bot/renovate-runner/-/raw/(?<currentValue>.*?)/templates/renovate\\.gitlab-ci\\.yml"],
"depNameTemplate": "renovate-bot/renovate-runner",
"datasourceTemplate": "gitlab-tags"
}
]
}
Configuration Renovate pour se mettre à jour lui-même sur GitLab
Si on active ce paramètre, il est aussi intéressant d’activer la migration automatique de configuration pour que Renovate mette à jour lui-même sa configuration lorsque des options sont renommées ou modifiées. Pour cela, il faut utiliser le paramètre "configMigration": true. Lorsqu’un paramètre sera mis à jour, Renovate ouvrira une Merge Request pour proposer le changement.
Certaines dépendances (comme Renovate lui-même) sortent des nouvelles versions de manière régulière. Dans ces cas-là, l’exécution de Renovate peut être ralentie le temps que toutes les notes de versions soient lues. Pour éviter cela, le paramètre fetchChangeLogs permet de ne pas les récupérer. En le combinant avec le paramètre packageRules que nous avons vu précédemment, il est possible d’ignorer la récupération de ces notes de version uniquement pour certaines dépendances qui comportent beaucoup de releases et de le garder pour toutes les autres qui sont plus légères.
{
"packageRules": [
{
"matchDepNames": ["renovate-bot/renovate-runner"],
"fetchChangeLogs": "off"
}
]
}
Ignorer la récupération des notes de version pour Renovate
Pour finir, il est nécessaire de bien réfléchir à quelle fréquence Renovate doit s’exécuter. Il est par exemple inutile de le lancer toutes les heures si on n’applique les mises à jour qu’une fois par semaine. De plus, cela peut lancer un pipeline de build lors de la mise à jour d’une Merge Request, et donc consommer des ressources inutilement.
Avec le preset recommandé par défaut, Renovate ne va ouvrir que 2 Merge Requests maximum par heure, il peut donc être intéressant de mettre le paramètre prHourlyLimit à 0 pour lever cette limite.
On peut ne pas le lancer le weekend ou la nuit avec le paramètre schedule qui prend en compte une syntaxe cron ou des mots clés. Par exemple, after 9am and before 6pm every weekday autorisera la création de Merge Request par Renovate de 9h à 18h en semaine. Cette restriction peut également se faire au niveau de la planification de l’exécution de Renovate (directement dans le cron ou le pipeline schedule par exemple).
Renovate est un outil puissant pour mettre à jour les dépendances de versions, qu’elles soient applicatives, d’infrastructure ou même dans des cas plus génériques. Cela permet d’éviter de potentielles failles de sécurité apportées par des bibliothèques externes dont dépendent nos codes.
L’outil est également très flexible et peut s’adapter à tous les projets sur lesquels on veut l’utiliser. On peut d’ailleurs le faire fonctionner avec la plupart des plateformes d’hébergement de code, là où certains de ses concurrents comme Dependabot ne fonctionnent que sur une plateforme donnée (GitHub en l’occurrence). La configuration par défaut fonctionne bien pour des cas simples, et si on veut faire des choses plus poussées, il suffit de consulter la documentation (bien fournie) de Renovate : https://docs.renovatebot.com.
Dans cet article, nous vous avons proposé quelques-unes de ces options que nous utilisons nous-même et qui permettent d’améliorer l’efficacité de Renovate. Même si elles ne couvrent bien évidemment pas tous les cas d’utilisation, elles permettent néanmoins d’améliorer la configuration par défaut, bien qu’elle soit déjà très efficace.