# Using renv with continuous integration

When building, deploying, or testing an renv-using project with continuous integration (CI) systems (e.g. GitHub Actions, 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 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` action is provided for projects using renv. To use this action, you can add the following steps to your workflow:

    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:

    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 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:

    # 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:

    # 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:

    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()"