Jeudi dernier, je me suis lancé un petit défi perso.
La veille, on avait décidé d’ajouter une fonctionnalité importante dans un projet : Pouvoir sauvegarder et reprendre la conversation d’un chatbot codé avec ellmer
.
Le challenge ? On voit le client à 10h pour un point de suivi.
Bon, pas de pression, ce n’est pas vital pour le rendez-vous, mais c’est toujours sympa de pouvoir dire « Oui, c’est déjà prêt ».
Je me chauffe, je branche Spotify, et je fonce dans le code.
J’avais déjà repéré une Merge Request toute fraîche dans le package qui faisait plus ou moins ce qu’il me fallait.
Je l’installe, je bricole, je teste… et ça marche.
Alors je commit, je pousse, la pipeline passe, et je file en visio, tout content de mon « exploit ».
Le call se passe bien, je pars faire mon sport, et je reviens l’après-midi, prêt à continuer.
Et là… message de Léo sur notre Mattermost :
COMMENT ÇA ÇA MARCHE PAS ?
Je vérifie chez moi, tout roule.
« Ça marche sur ma machine », que je lui dis.
« OK, mais sur la mienne, non. »
Je commence à m’énerver, je soulève mon bureau (╯°□°)╯︵ ┻━┻, et là je réalise :
On n’a plus la même version du package ellmer
.
On est exactement tombés dans le piège des gens qui n’utilisent pas renv
. Dans mon rush, j’avais oublié la mini étape de mettre à jour le renv.lock
.
Résultat : chez moi ça marche, mais chez Léo, rien ne passe.
Comment ça, la même chose vous est déjà arrivé ? Et vous n’utilisez pas renv
??
Ou alors, vous l’avez déjà installé mais vous ne savez pas trop à quoi il sert, ni comment l’utiliser au quotidien ?
Bon, OK, c’est justement le sujet de cet article. On va voir :
- Pourquoi
renv
est indispensable pour tout projet R un minimum sérieux (et pas seulement en équipe) - Comment il fonctionne concrètement
- Et comment résoudre tous les problèmes que vous pourriez avoir avec
C’est parti !
À quoi ça sert renv
?
Avoir les mêmes versions des packages sur tous vos environnements
C’est LA base.
renv
permet de garantir que tout le monde travaille avec exactement les mêmes versions de packages : vous, vos collègues, la machine de prod, la machine de test, etc.
Concrètement, ça veut dire quoi ?
- Plus de « ça marche sur ma machine » (mais pas sur celle du voisin)
- Sur l’onboarding d’un nouveau collègue, il a juste à cloner le dépôt de code, lancer
renv::restore()
, et c’est tout. Il a exactement le même environnement que tout le monde, sans prise de tête - Pour le déploiement en prod, même chose : le code tournera exactement de la même manière qu’en local
- Et surtout, pour de l’ancien code, qu’il soit vieux de 6 mois ou 6 ans, vous repartez d’exactement le même endroit qu’il y a 6 ans.
Bref : On élimine tous les bugs invisibles qui nous font perdre des heures et qui sont bêtement liés à des différences de versions de packages.
Utiliser des versions différentes selon vos projets
Sur une machine, tous les projets R partagent le même ensemble de packages par défaut.
C’est pratique… jusqu’au jour où c’est pas pratique. Vous mettez à jour un package pour un projet, et ça casse un autre projet à côté.
Chez nous à Data Champ’, on travaille sur des dizaines de projets en parallèle toute l’année. Ce serait impossible de retester toutes les applis dès qu’on met à jour un package.
Avec renv
, chaque projet a son propre environnement isolé :
- On peut bosser sur un vieux projet qui tourne avec R 3.6.3 et les packages d’époque, tout en profitant des dernières versions sur les autres projets.
- On évite les conflits entre projets. On n’a pas toujours le luxe de choisir de travailler avec la dernière version de R et des packages.
- Pour n’importe quel projet, on sait qu’on a juste à le cloner, à restaurer l’environnement, et on est sûr que tout fonctionnera comme prévu.
Ce n’est même pas une question d’être en équipe. C’est un outil de productivité qui facilite la vie en général pour tous les utilisateurs de R.
Et en plus, c’est très simple à utiliser !
Comment utiliser renv
?
Il y a trois verbes fondamentaux à connaître. C’est tout.
Initialiser renv
avec renv::init()
Quand un client m’envoie le code d’un projet, c’est la toute première chose que je fais.
Si le projet est un package, alors renv
vous propose d’utiliser le fichier DESCRIPTION
pour installer les packages.
> renv::init()
This project contains a DESCRIPTION file.
Which files should renv use for dependency discovery in this project?
1: Use only the DESCRIPTION file. (explicit mode)
2: Use all files in this project. (implicit mode)
Selection:
Nous on utilise rarement une structure de package pour nos applis Shiny, et en plus rien ne nous dit que le fichier DESCRIPTION
est bien à jour, donc j’ai plutôt envie de privilégier l’option 2 (mode implicite).
L’option 2 va scanner l’ensemble des fichiers du projets, et détecter automatiquement tous les packages utilisés :
- Quand vous utilisez
library(package)
- Ou bien l’écriture
package::function_name()
Les packages vont ensuite être téléchargés, et installés, automatiquement. Vous n’avez rien à faire de plus.
Un message de succès va s’afficher :
The version of R recorded in the lockfile will be updated:
- R [* -> 4.4.3]
- Lockfile written to "~/path/renv.lock".
- renv activated -- please restart the R session.
Deux informations sont importantes ici :
renv
a enregistré la version de R utilisée (la 4.4.3)renv
vous informe qu’il faut redémarrer la session R. C’est nécessaire après l’activation derenv
.
Lors de son initialisation, renv
a ajouté quelques fichiers dans votre environnement :
- Le fichier
renv.lock
est le plus important. Il contient la liste exhaustive de tous les packages nécessaires à votre code et les versions utilisées. - Une ligne dans le fichier
.Rprofile
(qu’il crée s’il n’existe pas) permettant d’activerrenv
automatiquement au démarrage de la session. - Le dossier
renv
contient des fichiers techniques et les packages installés. Il contient aussi un fichier.gitignore
qui exclut automatiquement ce qu’il faut.
Il n’est pas nécessaire de rajouter le dossier renv
dans le .gitignore
. Vous n’avez rien à faire de spécial à ce niveau.
Tout ce qui vous avez à faire est de lancer renv::init()
, c’est tout.
Sauvegarder les nouveaux packages avec renv::snapshot()
Au fil de votre développement, vous allez rajouter ou enlever des dépendances à certains packages. Ou bien vous pouvez aussi décider de mettre à jour certains packages.
Dans ce cas, pour que l’information du nouveau package soit bien enregistrée, il faut utiliser la commande renv::snapshot()
renv
va vous présenter les changements qui vont avoir lieu :
- Les packages ajoutés
- Les packages dont la version change
- Les packages enlevés
> renv::snapshot()
The following package(s) will be updated in the lockfile:
# CRAN -----------------------------------------------------------------------
- anytime [0.3.9 -> *]
- BH [1.81.0-1 -> *]
- bookdown [0.36 -> 0.43]
- brio [1.1.3 -> 1.1.5]
...
- xfun [0.41 -> 0.52]
- XML [3.99-0.15 -> 3.99-0.18]
- zip [2.3.0 -> 2.3.3]
- textshaping [* -> 1.0.1]
Do you want to proceed? [Y/n]:
Une fois que vous validez, le fichier renv.lock
est mis à jour. Il peut alors être ajouté dans un commit pour que les collègues le récupèrent.
C’est exactement cette étape que j’avais oublié dans mon histoire en début d’article !
Et du coup, comment ils font les collègues pour installer les packages ?
Restaurer l’environnement avec renv::restore()
Lorsque vous récupérez un projet qui utilise renv
, il va s’activer tout seul dès le lancement de la session :
# Bootstrapping renv 1.1.4 ---------------------------------------------------
- Downloading renv ... OK
- Installing renv ... OK
ℹ Using R 4.4.3 (lockfile was generated with R 4.1.2)
- Project '~/path' loaded. [renv 1.1.4]
- One or more packages recorded in the lockfile are not installed.
- Use `renv::status()` for more details.
R version 4.4.3 (2025-02-28) -- "Trophy Case"
Platform: x86_64-pc-linux-gnu (64-bit)
Plusieurs informations importantes sont affichées ici :
- Il a automatiquement installé
renv
en version 1.1.4. Très bien. - On m’informe que j’utilise R 4.4.3 alors que le lockfile (c’est-à-dire le fichier
renv.lock
) a été généré avec R 4.1.2. Là j’ai un problème, puisque je n’ai pas la bonne version de R. - On me dit aussi que certains packages enregistrés dans le lockfile ne sont pas installés. C’est normal, puisque je viens juste de lancer le projet.
- Finalement, on m’invite à lancer
renv::status()
pour en savoir plus.
Je relance le projet avec la bonne version de R cette fois-ci, puis je lance un renv::status()
:
> renv::status()
The following package(s) are in an inconsistent state:
package installed recorded used
anytime n y ?
AsioHeaders n y ?
askpass n y ?
backports n y ?
...
xtable n y ?
yaml n y ?
zip n y ?
zoo n y ?
See `?renv::status` for advice on resolving these issues.
Le renv::status()
m’affiche la liste des packages “problématiques” avec trois colonnes :
- Le package est-il installé ?
- Le package est-il sauvegardé dans le lockfile ?
- Le package est-il utilisé dans le projet ?
Dans mon cas ici, je restaure un projet. Donc tous les projets sont sauvegardés dans le lockfile et ne sont pas installés.
Pour les installer, rien de plus simple : renv::restore()
Mettre à jour tous les packages avec renv::update()
Dernière petite astuce. Si vous avez lu notre article « Quelle version de R faut-il utiliser en production ? », vous savez que c’est pas mal de régulièrement mettre à jour les packages. Disons une fois par an ou tous les deux ans.
Rien de plus simple : renv::update()
.
C’est tout.
La documentation
Cet article ne traite que d’un usage basique du package renv
(et honnêtement il n’y a pas grand chose de plus à savoir pour 99% du temps).
Si jamais vous avez besoin de creuser davantage, la documentation est par ici : Introduction to renv
Les problèmes et les solutions aux problèmes
renv
est vraiment un super outil.
Mais… il faut croire que gérer des arbres de dépendances dans tous les sens, ce n’est pas si évident.
Au-delà de la simplicité apparente des trois fonctions basiques pour un usage quotidien, vous risquez parfois de vous retrouver dans des situations un peu complexes.
C’est pourquoi j’ai rajouté cette section, avec tous les problèmes que j’ai moi-même rencontrés. J’entends enrichir cette section au fil des nouveaux problèmes qui apparaissent.
Et si vous-même avez un problème qui n’est pas traité ici, on se retrouve en bas dans la section Commentaires.
J’ai installé un package et renv
ne l’inclut pas dans le renv.lock
Vous avez fait :
install.packages("packagename")
- Puis :
renv::snapshot()
Et renv
ne vous propose pas de rajouter packagename
dans le lockfile.
C’est normal : renv
n’enregistre pas TOUS les packages que vous installez. Il enregistre seulement les packages qui sont utilisés dans l’application.
Donc si vous installez microbenchmark
pour vous aider à optimiser vos traitements de calcul, le package ne va pas être ajouté.
C’est une bonne chose : Inutile de surcharger les environnements de vos collègues ou en production avec des packages non utilisés.
Et si vous voulez VRAIMENT imposer à renv
d’ajouter le package ?
Alors je vous conseille de le faire de cette manière :
if (FALSE) library(microbenchmark) # Necessary to add in renv.lock
Le simple fait d’avoir écrit library(microbenchmark)
suffit à renv
pour inclure le package.
Mais : Le package n’est jamais chargé. Vous économisez ces précieuses millisecondes de chargement.
Et le commentaire est utile pour vos collègues qui ne comprendront peut-être pas pourquoi cette ligne existe et pourraient être tentés de la supprimer.
Les packages utilisés dans mes fichiers R Markdown ne sont pas détectés
Ce problème survient si le package yaml
n’est pas installé. D’ailleurs, vous avez peut-être reçu le warning suivant :
Warning message:
The 'yaml' package is required to parse dependencies within R Markdown files
Consider installing it with `install.packages("yaml")`.
Dans ce cas, installez le package yaml
.
Erreur : package ‘name’ is not available
En général, cette erreur survient lors d’un renv::init()
. Votre code utilise certains packages que renv
ne sait pas où aller chercher parce qu’ils ne sont pas sur le CRAN.
J’ai par exemple eu le cas avec le package polars
, qui est accessible depuis le R-multiverse :
The following package(s) were not installed successfully:
- [polars]: package 'polars' is not available
Dans ce cas, installez le package depuis la source où il est accessible, en général Github ou un autre repository que le CRAN.
Commentaires