---
title: "Using renv with continuous integration"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Using renv with continuous integration}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r, include=FALSE}
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>",
  eval = FALSE
)
```

When building, deploying, or testing an renv-using project with continuous 
integration (CI) systems (e.g. [GitHub Actions][github-actions],
[GitLab CI][gitlab-ci], and others) you need some way to tell the CI system
to use renv to restore the same packages that you're using locally.

The general idea is:

1. Call `renv::snapshot()` on your local machine to generate `renv.lock`.

2. Call `renv::restore()` on your CI service to restore the project library
   from `renv.lock`.

3. Cache the project library and global renv cache on the CI service.

Note that this workflow is not generally a good fit for CRAN packages, because 
CRAN itself runs `R CMD check` using the latest version of all dependencies.

## GitHub actions

Here, we describe two common approaches for integrating renv with a [GitHub Actions](https://github.com/features/actions) workflow:

* Use the `r-lib/setup-renv` action.
* Use GitHub's built-in cache action together with existing renv functionality;

### Using r-lib/actions/setup-renv

The r-lib organization offers some actions for R users, and among them a [`setup-renv`][r-lib-actions-renv] action is provided for projects using renv. To use this action, you can add the following steps to your workflow: 

```yaml
steps:
- uses: actions/checkout@v5
- uses: r-lib/actions/setup-r@v2
- uses: r-lib/actions/setup-renv@v2
```

Using these steps will automatically perform the following actions:

* renv will be installed, via `install.packages("renv")`,
* renv will be configured to use the GitHub cache,
* If provided via a `with: profile:` key, that renv profile will be activated,
* The project will be restored via `renv::restore()`.

After this, any steps using R will use the active renv project by default.

### Using the GitHub Actions Cache with renv

When using renv in your own custom GitHub action workflow, there are two main requirements:

1. Cache any packages installed by renv across runs, 
2. Use `renv::restore()` to restore packages using this cache to speed up installation

As an example, these steps might look like:

```yaml
env:
  RENV_PATHS_ROOT: ~/.cache/R/renv

steps:

- name: Cache packages
  uses: actions/cache@v4
  with:
    path: ${{ env.RENV_PATHS_ROOT }}
    key: ${{ runner.os }}-renv-${{ hashFiles('**/renv.lock') }}
    restore-keys: |
      ${{ runner.os }}-renv-

- name: Restore packages
  shell: Rscript {0}
  run: |
    if (!requireNamespace("renv", quietly = TRUE)) install.packages("renv")
    renv::restore()
```

See also the [example][github-actions-renv] on GitHub actions.

## Generating a lockfile in CI

In some CI workflows, you may want to generate or update a lockfile without
installing packages -- for example, to validate that dependencies can be
resolved, or to produce a lockfile artifact in a lightweight container that
doesn't have compilation tools.

`renv::plan()` resolves the packages required by a project and writes a
lockfile, without installing anything:

```r
# resolve all project dependencies and write renv.lock
renv::plan()

# or, resolve a specific set of packages
renv::plan(c("dplyr", "ggplot2"))
```

Because `renv::plan()` works from repository metadata rather than installed
packages, it can run in environments where package installation would be
impractical.

## Downloading packages in CI

In multi-stage CI pipelines, it can be useful to download packages in one
step and install them in a later step. `renv::retrieve()` downloads the
packages required by a project (or a lockfile) without installing them:

```r
# download all packages required by the lockfile
renv::retrieve()

# download specific packages
renv::retrieve(c("dplyr", "ggplot2"))
```

Downloaded packages are stored in renv's local cache, so a subsequent
`renv::restore()` can install from the cache without re-downloading.

## GitLab CI

The following template can be used as a base when using renv with
[GitLab CI][gitlab-ci]:

```yaml
variables:
  RENV_PATHS_ROOT: ${CI_PROJECT_DIR}/renv

cache:
  key: ${CI_PROJECT_NAME}
  paths:
    - ${RENV_PATHS_ROOT}

before_script:
  - < ... other pre-deploy steps ... >
  - Rscript -e "if (!requireNamespace('renv', quietly = TRUE)) install.packages('renv')"
  - Rscript -e "renv::restore()"
```


[gitlab-ci]: https://about.gitlab.com/solutions/continuous-integration/
[github-actions]: https://github.com/features/actions
[github-actions-renv]: https://github.com/actions/cache/blob/main/examples.md#r---renv
[r-lib-actions-renv]: https://github.com/r-lib/actions/tree/v2-branch/setup-renv
