Key message
If a project uses package renv, all R scripts should
start with the line
command::use_renv()Introduction
One obstacle to creating reproducible workflows in R is that the behavior of R packages can differ across versions. An R script that behaves one way if it is run using version 1.0 of a package may behave a different way if it is run using version 2.0 of the package. The renv package solves this problem by allowing users to lock down the exact versions of the packages a project uses.
The mechanism that renv uses to lock packages down is,
however, bypassed by Rscript
or littler. A
workflow that uses Rscript or littler needs to be tweaked before
renv performs correctly. Function
command::use_renv() helps with this tweaking.
How renv can fail with Rscript and littler
A project that uses renv contains a script
renv/activate.R. That script tells R which package versions
to use when running project code. renv only performs
correctly if R runs renv/activate.R when the session
starts.
renv arranges for renv/activate.R to be run
at startup by adding instructions to a file called
.Rprofile. Normally, when R is launched, it looks for
.Rprofile and follows any instructions the file contains,
including sourcing renv/activate.R.
If R is launched from Rscript or littler,
however, it does not look for an .Rprofile file.
Since it ignores .Rprofile, it does not run
renv/activate.R, and renv does not work.
command::use_renv() fixes the problem
The problem of Rscript and littler
bypassing renv/activate.R can be fixed by including the
line command::use_renv() at the top of each R script in the
project, as in,
command::use_renv()
suppressPackageStartupMessages({
library(dplyr)
library(command)
})
cmd_assign(.data = "data/cleaned.rds",
n_iter = 2000L,
.out = "out/model.rds")
# ... rest of script ...Function use_renv() looks for renv
infrastructure and, if it finds it, sources
renv/activate.R. The sourcing occurs later than it would in
a normal .Rprofile startup, but early enough for subsequent
library() calls to see the project library.
If use_renv() does not find any renv
infrastructure, it simply returns NULL. Including
command::use_renv() in projects that do not use
renv does no harm.
The exact format matters
The call to command::use_renv() must come before any
calls to library(). If library() has already
been called, then renv/activate.R does not work
properly.
The prohibition against calling library() before
command::use_renv() includes not calling
library(command). Use the :: operator
instead:
command::use_renv()
suppressPackageStartupMessages({
library(dplyr)
library(command)
})
# ... rest of script ...Do not load command first: