Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use RSPM with (macOS) fallback to CRAN #1048

Closed
salim-b opened this issue Jul 20, 2022 · 4 comments
Closed

Use RSPM with (macOS) fallback to CRAN #1048

salim-b opened this issue Jul 20, 2022 · 4 comments

Comments

@salim-b
Copy link
Contributor

salim-b commented Jul 20, 2022

RStudio Public Package Manager (RSPM) is an awesome service to the community, no doubt. It's main drawback compared to CRAN: No binaries for macOS.

renv seems to have RSPM integration to some degree, which is great. But I can't figure out a sane way to use the RSPM CRAN mirror URL (https://packagemanager.rstudio.com/cran/latest) for Linux and Windows, and at the same time the regular CRAN URL (https://cloud.r-project.org) for macOS (or the RSPM mirror only for Linux and regular CRAN also for Windows, doesn't really matter I guess).

I'm using the explicit snapshot type for my project (with a DESCRIPTION file listing immediate dependencies) and tried various different configurations to achieve the above goal. The nearest I could get is

  1. setting regular CRAN, i.e.

    "R": {
      "Repositories": [
        {
          "Name": "CRAN",
          "URL": "https://cloud.r-project.org"
        }
      ]
    }

    in the renv.lock file;

  2. conditionally overwriting the repos option in the project's .Rprofile after activating renv when on Linux, i.e.

    source("renv/activate.R")
    
    if (Sys.info()["sysname"] == "Linux") {
      options(repos =
                getOption("repos") |>
                magrittr::inset("CRAN", "https://packagemanager.rstudio.com/cran/latest") |>
                renv:::renv_rspm_transform())
    }

With the above config on Linux (Ubuntu 22.04 in my case), the weird thing is that when I e.g. do

renv::purge("sp", prompt = FALSE)
Output
The following packages will be purged from the cache:

	sp 1.5-0 [Hash: 2a118bdd2db0a301711e0a9e3e3206d5]

* Removed 1 package from the cache.

and then do

renv::repair()
Output
The following package(s) have broken symlinks into the cache:

	sp

Use `renv::repair()` to try and reinstall these packages.

* Querying repositories for available source packages ... Done!
Retrieving 'https://packagemanager.rstudio.com/cran/__linux__/jammy/latest/src/contrib/sp_1.5-0.tar.gz' ...
	OK [file is up to date]
Installing sp [1.5-0] ...
	OK [installed binary]
Moving sp [1.5-0] into the cache ...
	OK [moved to cache in 0.31 milliseconds]

the package sp is fetched from RSPM as can be seen in the output above (fine!).

But when I instead run

renv::restore(clean = TRUE,
              prompt = FALSE)
Output
The following package(s) have broken symlinks into the cache:

	sp

Use `renv::repair()` to try and reinstall these packages.

The following package(s) will be updated:

# CRAN ===============================
- sp   [* -> 1.5-0]

* Querying repositories for available source packages ... Done!
Retrieving 'https://cloud.r-project.org/src/contrib/sp_1.5-0.tar.gz' ...
	OK [downloaded 1009.7 Kb in 0.2 secs]
Installing sp [1.5-0] ...
	OK [built from source]
Moving sp [1.5-0] into the cache ...
	OK [moved to cache in 0.34 milliseconds]

after the purge, the package sp is still fetched from regular CRAN, as can again be seen in the output above (this sucks...).


The entry for package sp in the project's renv.lock file looks as follows:

"sp": {
  "Package": "sp",
  "Version": "1.5-0",
  "Source": "Repository",
  "Repository": "CRAN",
  "Hash": "2a118bdd2db0a301711e0a9e3e3206d5",
  "Requirements": [
    "lattice"
  ]
}

Is the differing behavior between renv::repair() and renv::restore() above the intended behaviour? If so, why? And would it still be possible to fetch packages from RSPM on some platforms and from CRAN on others within a single renv project?

@kevinushey
Copy link
Collaborator

The main difference between repair() and restore() is that repair() uses install(), and there are some subtle differences in how repositories are handled. In particular:

  • install() uses, in order of precedence, [repos argument > RENV_CONFIG_REPOS_OVERRIDE > getOption("repos")]
  • restore() uses, in order of precedence, [repos argument > RENV_CONFIG_REPOS_OVERRIDE > The repositories defined in the lockfile > getOption("repos")].

In other words, install() doesn't consider the repositories defined in the lockfile, while restore() does. So I think the problem here is that your attempt to change the repos R option is effectively ignored by renv here, and so restore() still uses the lockfile repositories.

As a workaround, I believe you could set your repositories as e.g.

# set up the default repos for your platform
options(repos = < ... >)

# tell renv to use that during restore by default
options(renv.config.repos.override = getOption("repos"))

since renv doesn't have the notion of platform-specific repositories (or other platform-specific lockfile entries).

The main reason why we consider the lockfile repositories directly in restore() is that some users might want to call renv::restore() without explicitly activating a project, and so in that case the user intention is likely to use the repositories as defined in the lockfile.

@salim-b
Copy link
Contributor Author

salim-b commented Jul 20, 2022

Thank you very much for the clear explanation @kevinushey! Helped a lot to better understand what's going on under the hood. And I can properly work around the issue now 😄

since renv doesn't have the notion of platform-specific repositories (or other platform-specific lockfile entries).

Do you plan on supporting such a notion? Most straight-forward, I guess, would be to add support for an optional platforms key for R.Repositories in renv.lock. So we could configure repos for example as:

"R": {
  "Repositories": [
    {
      "Name": "CRAN",
      "URL": "https://packagemanager.rstudio.com/cran/latest",
      "platforms": ["Windows", "Linux"]
    },
    {
      "Name": "CRAN",
      "URL": "https://cloud.r-project.org",
      "platforms": "Darwin"
    }
  ]
}

@angerhang
Copy link

Thank you very much for the clear explanation @kevinushey! Helped a lot to better understand what's going on under the hood. And I can properly work around the issue now 😄

since renv doesn't have the notion of platform-specific repositories (or other platform-specific lockfile entries).

Do you plan on supporting such a notion? Most straight-forward, I guess, would be to add support for an optional platforms key for R.Repositories in renv.lock. So we could configure repos for example as:

"R": {
  "Repositories": [
    {
      "Name": "CRAN",
      "URL": "https://packagemanager.rstudio.com/cran/latest",
      "platforms": ["Windows", "Linux"]
    },
    {
      "Name": "CRAN",
      "URL": "https://cloud.r-project.org",
      "platforms": "Darwin"
    }
  ]
}

Something like this will be very helpful

@hadley
Copy link
Member

hadley commented Apr 25, 2023

I think this is basically a duplicate of #1052

@hadley hadley closed this as completed Apr 25, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants