From d10f63689e11c43078e5cddd4c57b3d02f7dd834 Mon Sep 17 00:00:00 2001 From: Kevin Ushey Date: Tue, 22 Dec 2020 11:51:50 -0800 Subject: [PATCH] install now finds packages in local sources when possible --- DESCRIPTION | 2 +- NEWS.md | 4 + R/available-packages.R | 95 ++++++++++++------ R/retrieve.R | 6 +- R/scope.R | 6 ++ R/snapshot.R | 13 ++- .../local/skeleton/skeleton_1.0.0.tar.gz | Bin 0 -> 422 bytes .../local/skeleton/skeleton_1.0.0.tgz | Bin 0 -> 3035 bytes tests/testthat/test-available-packages.R | 14 +++ tests/testthat/test-install.R | 19 ++++ tests/testthat/test-snapshot.R | 8 +- 11 files changed, 131 insertions(+), 36 deletions(-) create mode 100644 tests/testthat/local/skeleton/skeleton_1.0.0.tar.gz create mode 100644 tests/testthat/local/skeleton/skeleton_1.0.0.tgz diff --git a/DESCRIPTION b/DESCRIPTION index fbac7a6c7..ff3419937 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Package: renv Type: Package Title: Project Environments -Version: 0.12.3-61 +Version: 0.12.3-62 Authors@R: c( person("Kevin", "Ushey", role = c("aut", "cre"), email = "kevin@rstudio.com"), person("RStudio", role = c("cph")) diff --git a/NEWS.md b/NEWS.md index 8b630df4d..851c4518a 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,6 +1,10 @@ # renv 0.13.0 (UNRELEASED) +* `renv::install("package")` will now install the latest-available version of + that package from local sources, if that package is available and newer than + any package available on the active package repositories. (#591) + * The configuration option `startup.quiet` has been added, allowing one to control whether `renv` will display the typical startup banner when a project is loaded. diff --git a/R/available-packages.R b/R/available-packages.R index e2fefd0f8..1abf71e36 100644 --- a/R/available-packages.R +++ b/R/available-packages.R @@ -217,7 +217,14 @@ renv_available_packages_record <- function(entry, type) { renv_available_packages_latest_impl <- function(package, type) { + # get available packages dbs <- renv_available_packages(type = type, quiet = TRUE) + + # prepend local sources if available + local <- renv_available_packages_local(type = type) + if (!is.null(local)) + dbs <- c(list(Local = local), dbs) + fields <- c("Package", "Version", "OS_type", "NeedsCompilation", "Repository") entries <- bapply(dbs, function(db) { @@ -268,39 +275,14 @@ renv_available_packages_latest_impl <- function(package, type) { }, index = "Name") - if (is.null(entries)) { - - # for diagnostics on CI - if (renv_tests_running() && renv_tests_verbose()) { - - # write error message - fmt <- "internal error: package '%s' not available on test repositories" - writef(fmt, package) - - # dump some (hopefully) helpful information - repos <- getOption("repos") - str(repos) - - dbs %>% map(`[`, c("Package", "Version")) %>% print() - - repopath <- gsub("^file:/+", "", repos) - files <- list.files( - path = repopath, - all.files = TRUE, - full.names = TRUE, - recursive = TRUE - ) - - writeLines(files) - - } - + if (is.null(entries)) return(NULL) - } # sort based on version version <- numeric_version(entries$Version) ordered <- order(version, decreasing = TRUE) + + # return newest-available version entries[ordered[[1]], ] } @@ -385,3 +367,60 @@ renv_available_packages_latest_select <- function(src, bin) { renv_available_packages_record(src, "source") } + +renv_available_packages_local <- function(type, project = NULL) { + + project <- renv_project_resolve(project) + + # list files recursively in the local sources paths + roots <- c( + renv_paths_project("renv/local", project = project), + renv_paths_local() + ) + + # find all files used in the locals folder + all <- list.files( + path = roots, + all.files = TRUE, + full.names = TRUE, + recursive = TRUE, + include.dirs = FALSE + ) + + # keep only files with matching extensions + ext <- renv_package_ext(type = type) + keep <- all[fileext(all) %in% ext] + + # read the DESCRIPTION files within the archive + descs <- lapply(keep, function(path) { + + # read the DESCRIPTION + desc <- renv_description_read(path) + + # set the Repository field + prefix <- if (renv_platform_windows()) "file:///" else "file://" + uri <- paste0(prefix, dirname(path)) + desc[["Repository"]] <- uri + + # return it + desc + + }) + + # extract DESCRIPTION fields of interest + fields <- c("Package", "Version", "OS_type", "NeedsCompilation", "Repository") + records <- map(descs, function(desc) { + + # ensure missing fields are set as NA + missing <- setdiff(fields, names(desc)) + desc[missing] <- NA + + # return record with requested fields + desc[fields] + + }) + + # bind into data.frame for lookup + bind_list(records) + +} diff --git a/R/retrieve.R b/R/retrieve.R index 53cfc4150..16b5bfcf9 100644 --- a/R/retrieve.R +++ b/R/retrieve.R @@ -343,10 +343,8 @@ renv_retrieve_local_report <- function(record) { if (source == "local") return(record) - record$Source <- "Local" - rather <- if (source == "unknown") "" else paste(" rather than", renv_alias(source)) - fmt <- "* Package %s [%s] will be installed from local sources%s." - with(record, vwritef(fmt, Package, Version, rather)) + fmt <- "* Package %s [%s] will be installed from local sources." + with(record, vwritef(fmt, Package, Version)) record diff --git a/R/scope.R b/R/scope.R index 35e945c3b..94843c360 100644 --- a/R/scope.R +++ b/R/scope.R @@ -327,3 +327,9 @@ renv_scope_lock <- function(path = NULL, defer(callback(), envir = parent.frame()) } + +renv_scope_trace <- function(what, tracer, ..., .envir = NULL) { + .envir <- .envir %||% parent.frame() + trace(what = substitute(what), tracer = tracer, print = FALSE, ...) + defer(untrace(what), envir = .envir) +} diff --git a/R/snapshot.R b/R/snapshot.R index 87a554960..af961db62 100644 --- a/R/snapshot.R +++ b/R/snapshot.R @@ -675,8 +675,17 @@ renv_snapshot_description_source <- function(dcf) { catch(renv_available_packages_latest(package, type = "source")) }) - if (!inherits(entry, "error")) - return(list(Source = "Repository", Repository = entry[["Repository"]])) + if (!inherits(entry, "error")) { + + # check for and handle local repositories + repos <- entry[["Repository"]] + if (identical(repos, "Local")) + return(list(Source = "Local")) + + # otherwise, treat as regular entry + return(list(Source = "Repository", Repository = repos)) + + } location <- catch(renv_retrieve_local_find(dcf)) if (!inherits(location, "error")) diff --git a/tests/testthat/local/skeleton/skeleton_1.0.0.tar.gz b/tests/testthat/local/skeleton/skeleton_1.0.0.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..596cad2a8e97e9b2f1ee079f96efa0072ed840fc GIT binary patch literal 422 zcmV;X0a^YZiwFP!000001MQPRZ`v>v$8+|lc2Nt)7xC4!|9Wulje2x#;SY@Am%TxErKrQWzDHh>uvSO2wiT=+V5l| zgHFHoU{O_71BC&l8^0{|A`ZeDb>U=)fBf+SBq1W}kV%=NcHshN*<`oQJ7lbLQ-m`T zu4kXRr(~iYnvwEKg0~waQ^djz&AKitwNI-Jd3;zQfm2SqjCVN)+Lz*;?6DAM_#XbW zoJ6mc-%UQ54lc%5*(Ps)-5S*T(uxd5=~fAKiYty}60MA9Y@@v07SM QT3$H60EcPd-T({$009cnr~m)} literal 0 HcmV?d00001 diff --git a/tests/testthat/local/skeleton/skeleton_1.0.0.tgz b/tests/testthat/local/skeleton/skeleton_1.0.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..676de2dee9d1fdfe64694ad7873028ef4da003e5 GIT binary patch literal 3035 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