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

Optionally install packages during snapshot() #1370

Merged
merged 22 commits into from
Jun 5, 2023
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@

# renv 0.18.0 (UNRELEASED)

* If `renv::snapshot()` finds missing packages, a new prompt allows you to
install them before continuing (#1198).

* `renv::dependencies()` no longer treats `box::use(module/file)` as using
package `module` (#1377).

Expand Down
37 changes: 28 additions & 9 deletions R/lockfile.R
Original file line number Diff line number Diff line change
Expand Up @@ -174,17 +174,36 @@ renv_lockfile_create <- function(project,

lockfile <- renv_lockfile_init(project)

renv_lockfile_records(lockfile) <-

renv_snapshot_libpaths(libpaths = libpaths,
project = project) %>%
records <- renv_snapshot_libpaths(libpaths = libpaths, project = project)

records <- tryCatch(
renv_snapshot_filter(
project = project,
records = records,
type = type,
packages = packages,
exclude = exclude
),
renv_recompute_records = function(cnd) {
NULL
}
)

renv_snapshot_filter(project = project,
type = type,
packages = packages,
exclude = exclude) %>%
# We re-computing and re-filtering records once, if requested by user
if (is.null(records)) {
renv_dynamic_reset()
records <- renv_snapshot_libpaths(libpaths = libpaths, project = project)
records <- renv_snapshot_filter(
project = project,
records = records,
type = type,
packages = packages,
exclude = exclude
)
}

renv_snapshot_fixup()
records <- renv_snapshot_fixup(records)
renv_lockfile_records(lockfile) <- records

lockfile <- renv_lockfile_fini(lockfile, project)

Expand Down
23 changes: 19 additions & 4 deletions R/snapshot.R
Original file line number Diff line number Diff line change
Expand Up @@ -943,21 +943,36 @@ renv_snapshot_filter_report_missing <- function(missing, type) {

postamble <- c(
"Packages must first be installed before renv can snapshot them.",
"Consider installing these packages using `renv::install()`.",
if (type %in% "explicit")
"If these packages are no longer required, consider removing them from your DESCRIPTION file."
else
"Use `renv::dependencies()` to see where this package is used in your project."
)
renv_pretty_print(
values = csort(unique(missing)),
values = sort(unique(missing)),
preamble = preamble,
postamble = postamble
)

cancel_if(interactive() && !proceed())
TRUE
choices <- c(
"Snapshot, just using the currently installed packages",
"Install the packages, then snapshot",
"Cancel"
)
choice <- menu(choices, title = "What do you want to do?")
cancel_if(choice %in% c(0, 3))

if (choice == 2L) {
install(missing, prompt = FALSE)
# User will only see this in exceptional circumstances as it is
# caught once by renv_lockfile_create()
stop(errorCondition(
message = "Failed to restart snapshotting after install",
class = "renv_recompute_records"
))
}

TRUE
}

renv_snapshot_filter_implicit <- function(project, records) {
Expand Down
34 changes: 34 additions & 0 deletions R/utils.R
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,40 @@ proceed <- function(default = TRUE) {
ask("Do you want to proceed?", default = default)
}

menu <- function(choices, title, default = 1L) {
testing <- getOption("renv.menu.choice", integer())
if (length(testing)) {
selected <- testing[[1]]
options(renv.menu.choice = testing[-1])
} else if (is_testing()) {
selected <- default
} else {
selected <- NULL
}

if (!is.null(selected)) {
writef(c(
title,
"",
paste0(seq_along(choices), ": ", choices),
"",
paste0("Selection: ", selected),
""
))
return(selected)
}

if (!interactive()) {
writef(c("Not interactive. Will:", choices[[default]]))
return(default)
}

tryCatch(
utils::menu(choices, title, graphics = FALSE),
interrupt = function(cnd) 0
)
}

# nocov end

inject <- function(contents,
Expand Down
80 changes: 76 additions & 4 deletions tests/testthat/_snaps/snapshot.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,16 @@
- oatmeal

Packages must first be installed before renv can snapshot them.
Consider installing these packages using `renv::install()`.
Use `renv::dependencies()` to see where this package is used in your project.

What do you want to do?

1: Snapshot, just using the currently installed packages
2: Install the packages, then snapshot
3: Cancel

Selection: 1

The following package(s) will be updated in the lockfile:

# CRAN ===============================
Expand All @@ -41,9 +48,16 @@
- oatmeal

Packages must first be installed before renv can snapshot them.
Consider installing these packages using `renv::install()`.
Use `renv::dependencies()` to see where this package is used in your project.

What do you want to do?

1: Snapshot, just using the currently installed packages
2: Install the packages, then snapshot
3: Cancel

Selection: 1

The following package(s) will be updated in the lockfile:

# CRAN ===============================
Expand Down Expand Up @@ -75,9 +89,16 @@
- breakfast

Packages must first be installed before renv can snapshot them.
Consider installing these packages using `renv::install()`.
If these packages are no longer required, consider removing them from your DESCRIPTION file.

What do you want to do?

1: Snapshot, just using the currently installed packages
2: Install the packages, then snapshot
3: Cancel

Selection: 1

* The lockfile is already up to date.

# snapshot() warns when required package is not installed
Expand All @@ -90,9 +111,16 @@
- breakfast

Packages must first be installed before renv can snapshot them.
Consider installing these packages using `renv::install()`.
Use `renv::dependencies()` to see where this package is used in your project.

What do you want to do?

1: Snapshot, just using the currently installed packages
2: Install the packages, then snapshot
3: Cancel

Selection: 1

The following package(s) will be updated in the lockfile:

# CRAN ===============================
Expand Down Expand Up @@ -126,6 +154,50 @@
- R [4.1 -> 4.2]


# user can choose to install missing packages

Code
snapshot()
Output
The following required packages are not installed:

- egg

Packages must first be installed before renv can snapshot them.
Use `renv::dependencies()` to see where this package is used in your project.

What do you want to do?

1: Snapshot, just using the currently installed packages
2: Install the packages, then snapshot
3: Cancel

Selection: 2

Downloading 1 package(s) and their dependencies
Retrieving 'file://<test-repo>/src/contrib/egg_1.0.0.tar.gz' ...
OK [downloaded 260 bytes in XXXX seconds]
Done

The following package(s) will be installed:

- egg [1.0.0]

Packages will be installed into "<tempdir>/<renv-library>"

Installing egg ... OK [built from source]
Caching egg ... OK [copied]
Installed 1 package in XXXX seconds.
The following package(s) will be updated in the lockfile:

# CRAN ===============================
- egg [* -> 1.0.0]

The version of R recorded in the lockfile will be updated:
- R [* -> <r-version>]

* Lockfile written to '<wd>/renv.lock'.

# useful error message if implicit dep discovery is slow

Code
Expand Down
2 changes: 1 addition & 1 deletion tests/testthat/helper-snapshot.R
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ expect_snapshot <- function(x, ...) {
strip_dirs <- function(x) {
x <- gsub(getwd(), "<wd>", x, fixed = TRUE)
x <- gsub(renv_paths_cache(), "<cache>", x, fixed = TRUE)
x <- gsub(renv_tests_repopath(), "<test-repo>", x, fixed = TRUE)
x <- gsub(normalizePath(tempdir(), winslash = "/"), "<tempdir>", x, fixed = TRUE)
x <- gsub(tempdir(), "<tempdir>", x, fixed = TRUE)
x <- gsub("renv-library-\\w+", "<renv-library>", x)
x <- gsub(getRversion(), "<r-version>", x, fixed = TRUE)
x <- gsub(renv_tests_repopath(), "<test-repo>", x, fixed = TRUE)
x <- gsub(renv_platform_prefix(), "<platform-prefix>", x, fixed = TRUE)
x
}
9 changes: 9 additions & 0 deletions tests/testthat/test-snapshot.R
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,15 @@ test_that("snapshot always reports on R version changes", {
})
})

test_that("user can choose to install missing packages", {

renv_tests_scope("egg")
renv_scope_options(renv.menu.choice = 2)

expect_snapshot(snapshot())
writeLines(library(egg), "deps.R")

})

test_that("useful error message if implicit dep discovery is slow", {

Expand Down