Skip to contents

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:

library(command)  ## not correct!!
use_renv()        ## not correct!!

suppressPackageStartupMessages({
  library(dplyr)
  library(command)
})

# ... rest of script ...