rstudio / renv

renv: Project environments for R.
https://rstudio.github.io/renv/
MIT License
982 stars 153 forks source link

`renv::update()` does not respect source repository with multiple repos #1918

Open kthayashi opened 1 month ago

kthayashi commented 1 month ago

I've found that renv::update() does not respect the recorded source repository for packages that have different versions available in different repos. I've prepared the following minimal reproducible example (https://github.com/kthayashi/stan-renv-reprex) to demonstrate this:

For this example, let's say that I'm working with three packages that are distributed as follows:

Some of these packages have dependencies (e.g. bayesplot) that also have the same distribution mode as loo. I would like to use release versions when possible, so I install brms and cmdstanr from CRAN and R-Universe, respectively. And let's say that I need a feature in the development version of loo, so I install loo from R-Universe.

renv version:

packageVersion("renv")
#> [1] ‘1.0.7’

Repo configuration:

getOption("repos")
#>                             CRAN                              Stan 
#>    "https://cloud.r-project.org" "https://stan-dev.r-universe.dev" 

Excerpts from renv.lock:

{
  "R": {
    "Version": "4.3.3",
    "Repositories": [
      {
        "Name": "CRAN",
        "URL": "https://cloud.r-project.org"
      },
      {
        "Name": "Stan",
        "URL": "https://stan-dev.r-universe.dev"
      }
    ]
  },
  "Packages": {

    "bayesplot": {
      "Package": "bayesplot",
      "Version": "1.11.1",
      "Source": "Repository",
      "Repository": "CRAN",
      "Requirements": [
        "R",
        "dplyr",
        "ggplot2",
        "ggridges",
        "glue",
        "posterior",
        "reshape2",
        "rlang",
        "stats",
        "tibble",
        "tidyselect",
        "utils"
      ],
      "Hash": "c5cde76012ea08e94265ace1b53b77b9"
    },

    "brms": {
      "Package": "brms",
      "Version": "2.21.0",
      "Source": "Repository",
      "Repository": "CRAN",
      "Requirements": [
        "Matrix",
        "R",
        "Rcpp",
        "abind",
        "backports",
        "bayesplot",
        "bridgesampling",
        "coda",
        "future",
        "future.apply",
        "ggplot2",
        "glue",
        "grDevices",
        "loo",
        "matrixStats",
        "methods",
        "mgcv",
        "nleqslv",
        "nlme",
        "parallel",
        "posterior",
        "rlang",
        "rstan",
        "rstantools",
        "stats",
        "utils"
      ],
      "Hash": "5997b296ee665c10f0d18f1d01c9dd5c"
    },

    "cmdstanr": {
      "Package": "cmdstanr",
      "Version": "0.8.0",
      "Source": "Repository",
      "Repository": "Stan",
      "Requirements": [
        "R",
        "R6",
        "checkmate",
        "data.table",
        "jsonlite",
        "posterior",
        "processx",
        "rlang",
        "withr"
      ],
      "Hash": "9976d47a9f361011fea7e631975b3ce4"
    },

    "loo": {
      "Package": "loo",
      "Version": "2.7.0.9000",
      "Source": "Repository",
      "Repository": "Stan",
      "Requirements": [
        "R",
        "checkmate",
        "matrixStats",
        "parallel",
        "posterior",
        "stats"
      ],
      "Hash": "3ad9ae849bbade5fe9c6d71a0257dfeb"
    }

  }
}

Based on the description for renv::update(), I expected packages installed from CRAN (with "Repository": "CRAN" in its renv.lock entry) will be updated according to the version available on CRAN:

Updates will only be checked from the same source – for example, if a package was installed from GitHub, but a newer version is available on CRAN, that updated version will not be seen.

However, renv::update() attempts to update packages to the newest version across all repos:

renv::update()
#> - Checking for updated packages ... Done!
#> The following package(s) will be updated:
#> 
#> # CRAN -----------------------------------------------------------------------
#> - boot          [1.3-29 -> 1.3-30]
#> - codetools     [0.2-19 -> 0.2-20]
#> - KernSmooth    [2.23-22 -> 2.23-24]
#> - lattice       [0.22-5 -> 0.22-6]
#> - survival      [3.5-8 -> 3.6-4]
#>
#> # Stan -----------------------------------------------------------------------
#> - bayesplot     [repo: CRAN -> Stan; ver: 1.11.1 -> 1.11.1.9000]
#> - posterior     [repo: CRAN -> Stan; ver: 1.5.0 -> 1.5.0.9000]
#> - rstan         [repo: CRAN -> Stan; ver: 2.32.6 -> 2.35.0.9000]
#> - rstantools    [repo: CRAN -> Stan; ver: 2.4.0 -> 2.4.0.9000]
#> - StanHeaders   [repo: CRAN -> Stan; ver: 2.32.9 -> 2.35.0.9000]

All the items listed under Stan here were initially installed from CRAN and recorded in renv.lock as such, but renv::update() attempts to replace these with development versions from R-Universe. I was wondering if this is unexpected behavior, or if this is simply a case where "Updates will only be checked from the same source" actually means CRAN (or CRAN-like repo) vs. GitHub with no way to distinguish between different sources in getOption("repos").

kevinushey commented 1 month ago

I was wondering if this is unexpected behavior, or if this is simply a case where "Updates will only be checked from the same source" actually means CRAN (or CRAN-like repo) vs. GitHub with no way to distinguish between different sources in getOption("repos").

Right, the intention was the latter -- for packages installed from a CRAN-like repository, any available CRAN-like repository will be considered a candidate when checking for updates. It seems reasonable to restrict this in certain scenarios, though -- it might take some thought to figure out how to best make that work.

kthayashi commented 1 month ago

Thank you for the clarification! I agree that it could be useful to track individual repos for updates, or at least restrict updates to certain repos. In the example above, it would be convenient if only CRAN packages could be updated without having to manually specify packages or exclude.