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 6 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, it will prompt you to first
install them (#1198).

* `renv::deactivate()` gains a `clean` argument: when `TRUE` it will delete
all renv files/directories, leaving the project the way it was found.

Expand Down
33 changes: 23 additions & 10 deletions R/lockfile.R
Original file line number Diff line number Diff line change
Expand Up @@ -174,17 +174,30 @@ renv_lockfile_create <- function(project,

lockfile <- renv_lockfile_init(project)

renv_lockfile_records(lockfile) <-

renv_snapshot_libpaths(libpaths = libpaths,
project = project) %>%

renv_snapshot_filter(project = project,
type = type,
packages = packages,
exclude = exclude) %>%
# Repeat with restarts so that renv_snapshot_filter() can install packages
# if requested by the user
repeat {
withRestarts(
{
records <- renv_snapshot_libpaths(libpaths = libpaths, project = project)
records <- renv_snapshot_filter(
project = project,
records = records,
type = type,
packages = packages,
exclude = exclude
)
# skipped if recomputeRecords restart is used
break
},
recomputeRecords = function() {
renv_dynamic_reset()
}
)
}

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

lockfile <- renv_lockfile_fini(lockfile, project)

Expand Down
19 changes: 14 additions & 5 deletions R/snapshot.R
Original file line number Diff line number Diff line change
Expand Up @@ -967,7 +967,6 @@ renv_snapshot_filter_impl <- function(project, records, packages, type) {
}

renv_snapshot_filter_report_missing <- function(missing, type) {

missing <- setdiff(missing, "renv")
if (empty(missing))
return(TRUE)
Expand All @@ -976,21 +975,31 @@ 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 = 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)
invokeRestart("recomputeRecords")
}

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 @@ -163,6 +163,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
73 changes: 69 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,3 +154,40 @@
- 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

Retrieving 'file://<tempdir>/renv-repos-519265f184d7/src/contrib/egg_1.0.0.tar.gz' ...
OK [downloaded 260 bytes in XXXX seconds]
Installing egg [1.0.0] ...
OK [built from source in XXXX seconds]
Copying egg [1.0.0] into the cache ...
OK [copied to cache in XXXX seconds]
Installed 1 package into library at path "<tempdir>/<renv-library>".
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'.

1 change: 1 addition & 0 deletions tests/testthat/helper-testthat.R
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ renv_test_state <- function() {
opts$ambiguousMethodSelection <- NULL
opts$restart <- NULL
opts$repos[opts$repos == "@CRAN@"] <- "https://cloud.r-project.org"
opts$
opts <- opts[csort(names(opts))]

envvars <- as.list(Sys.getenv())
Expand Down
10 changes: 10 additions & 0 deletions tests/testthat/test-snapshot.R
Original file line number Diff line number Diff line change
Expand Up @@ -440,3 +440,13 @@ test_that("snapshot always reports on R version changes", {
renv_snapshot_report_actions(list(), R4.1, R4.2)
})
})

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")

})