rstudio / rsconnect

Publish Shiny Applications, RMarkdown Documents, Jupyter Notebooks, Plumber APIs, and more
http://rstudio.github.io/rsconnect/
133 stars 81 forks source link

How do I deploy an ‘renv’ app from inside a *different* project? #1093

Open klmr opened 2 months ago

klmr commented 2 months ago

I am trying to deploy a Plumber API that is using ‘renv’ via rsconnect::deployApp(). I am calling deployApp() from outside that project because I don’t want to add the ‘rsconnect’ package as a dependency to my project (I want to keep its dependencies as lean as possible).

Unfortunately this fails since the packages inside the Plumber API renv have different versions than the ones outside the project:

rsconnect::deployApp('plumber-api', appId = app_id, acocunt = account, server = server)
── Preparing for deployment ────────────────────────────────────────────────────
[…]
ℹ Capturing R dependencies from renv.lock
Error in `parseRenvDependencies()`:
! Library and lockfile are out of sync
ℹ Use renv::restore() or renv::snapshot() to synchronise
ℹ Or ignore the lockfile by adding to your .rscignore
Backtrace:
    ▆
 1. └─rsconnect (local) deployApp(dir, appId = app_id, account = "default", server = "default")
 2.   └─rsconnect:::bundleApp(...)
 3.     └─rsconnect:::createAppManifest(...)
 4.       └─rsconnect:::bundlePackages(...)
 5.         └─rsconnect:::computePackageDependencies(...)
 6.           └─rsconnect:::parseRenvDependencies(bundleDir)
 7.             └─cli::cli_abort(...)
 8.               └─rlang::abort(...)

And indeed I can recapitulate this by looking at the package versions between my plumber-api project and the currently active project.

I also tried activating the plumber-api renv project before calling deployApp() via renv::load('plumber-api') but that doesn’t solve the issue, since parseRenvDependencies() internally calls packageDescription(…, lib.loc = NULL) to find the installed package version; however, for packages that are already loaded this will read the loaded package’s package description, not the ones from the suitable renv library.

Keeping the dependencies between these two projects in sync would be a non-trivial effort.

I believe the fix would be to replace

https://github.com/rstudio/rsconnect/blob/9999fdb11dde6d5efe596677f9a6be6bd779e31d/R/bundlePackageRenv.R#L51

with

  deps$description <- lapply(deps$Package, package_record, lib_dir = renv::paths$library(project = bundleDir))

I’m happy to create a PR but I don’t really know how to create a good, self-contained test case for that — I’d have to create a mock package and install two different versions of that in two different projects.

klmr commented 2 months ago

Actually bundleDir isn’t what I thought it is, and consequently renv::paths$library(project = bundleDir) doesn’t actually work. But using .libPaths() instead should work.