From 249390ca5ef0f035c77a89926b5ff3e66fc54dbe Mon Sep 17 00:00:00 2001 From: m7pr Date: Mon, 1 Jul 2024 12:13:42 +0200 Subject: [PATCH 01/85] substitute future::promises lockfile creation with callr::r_bg --- .pre-commit-config.yaml | 3 +-- DESCRIPTION | 9 ++++----- R/teal_lockfile.R | 40 +++++++++++++++++++++------------------- 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e8147f2302..7ed148fc70 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -17,14 +17,13 @@ repos: additional_dependencies: - davidgohel/flextable # Error: package 'flextable' is not available - davidgohel/gdtools # for flextable + - callr - checkmate - - future - jsonlite - lifecycle - logger - magrittr - methods - - promises - renv - rlang - shiny diff --git a/DESCRIPTION b/DESCRIPTION index f00d577ca8..9a96930dd8 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -40,14 +40,13 @@ Depends: teal.data (>= 0.5.0), teal.slice (>= 0.5.0) Imports: + callr (>= 3.7.6), checkmate (>= 2.1.0), - future (>= 1.33.2), jsonlite, lifecycle (>= 0.2.0), logger (>= 0.2.0), magrittr (>= 1.5), methods, - promises (>= 1.3.0), renv (>= 1.0.7), rlang (>= 1.0.0), shinyjs, @@ -74,9 +73,9 @@ VignetteBuilder: RdMacros: lifecycle Config/Needs/verdepcheck: rstudio/shiny, insightsengineering/teal.data, - insightsengineering/teal.slice, mllg/checkmate, - HenrikBengtsson/future, jeroen/jsonlite, r-lib/lifecycle, - daroczig/logger, tidyverse/magrittr, rstudio/promises, rstudio/renv, + insightsengineering/teal.slice, r-lib/callr, mllg/checkmate, + jeroen/jsonlite, r-lib/lifecycle, + daroczig/logger, tidyverse/magrittr, rstudio/renv, r-lib/rlang, daattali/shinyjs, insightsengineering/teal.logger, insightsengineering/teal.reporter, insightsengineering/teal.widgets, rstudio/bslib, yihui/knitr, bioc::MultiAssayExperiment, r-lib/R6, diff --git a/R/teal_lockfile.R b/R/teal_lockfile.R index 0c632a5298..c922fd39b2 100644 --- a/R/teal_lockfile.R +++ b/R/teal_lockfile.R @@ -40,22 +40,24 @@ teal_lockfile <- function() { } if (!(is_in_test() || is_r_cmd_check())) { - old_plan <- future::plan() - # If there is already a parallel (non-sequential) backend, reuse it. - if (inherits(old_plan, "sequential")) { - future::plan(future::multisession, workers = 2) - } - - lockfile_task <- ExtendedTask$new(create_renv_lockfile) - lockfile_task$invoke(close = inherits(old_plan, "sequential"), lockfile_path) + # old_plan <- future::plan() + # # If there is already a parallel (non-sequential) backend, reuse it. + # if (inherits(old_plan, "sequential")) { + # future::plan(future::multisession, workers = 2) + # } + # + # lockfile_task <- ExtendedTask$new(create_renv_lockfile) + # lockfile_task$invoke(close = inherits(old_plan, "sequential"), lockfile_path) + callr::r_bg(create_renv_lockfile, args = list(lockfile_path = lockfile_path)) logger::log_trace("lockfile creation invoked.") } } -create_renv_lockfile <- function(close = FALSE, lockfile_path = NULL) { - checkmate::assert_flag(close) +create_renv_lockfile <- function(#close = FALSE, + lockfile_path = NULL) { + #checkmate::assert_flag(close) checkmate::assert_string(lockfile_path, na.ok = TRUE) - promise <- promises::future_promise({ + #promise <- promises::future_promise({ # Below we can not use a file created in tempdir() directory. # If a file is created in tempdir(), it gets deleted on 'then(onFulfilled' part. shiny::onStop(function() file.remove(lockfile_path)) @@ -75,14 +77,14 @@ create_renv_lockfile <- function(close = FALSE, lockfile_path = NULL) { } lockfile_path - }) - if (close) { - # If the initial backend was only sequential, bring it back. - promises::then(promise, onFulfilled = function() { - future::plan(future::sequential) - }) - } - promise + #}) + # if (close) { + # # If the initial backend was only sequential, bring it back. + # promises::then(promise, onFulfilled = function() { + # future::plan(future::sequential) + # }) + # } + # promise } teal_lockfile_downloadhandler <- function() { From d1efcc7f7acb70707a08198e6429866bb1aaccf5 Mon Sep 17 00:00:00 2001 From: m7pr Date: Mon, 1 Jul 2024 15:13:29 +0200 Subject: [PATCH 02/85] fix tests --- tests/testthat/test-utils.R | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/testthat/test-utils.R b/tests/testthat/test-utils.R index 505cc14fec..cad55e8d0f 100644 --- a/tests/testthat/test-utils.R +++ b/tests/testthat/test-utils.R @@ -254,12 +254,10 @@ testthat::test_that("defunction recursively goes down a list", { }) testthat::test_that("create_renv_lockfile creates a lock file during the execution", { - old_plan <- future::plan(future::sequential) - withr::defer(future::plan(old_plan)) renv_file_name <- "teal_app.lock" withr::defer(file.remove(renv_file_name)) - promise <- create_renv_lockfile(TRUE, renv_file_name) + teal:::create_renv_lockfile(renv_file_name) testthat::expect_true(file.exists(renv_file_name)) }) From 9bd99442788ee1a0b4db8f4ae928a021140ae987 Mon Sep 17 00:00:00 2001 From: m7pr Date: Mon, 1 Jul 2024 15:13:42 +0200 Subject: [PATCH 03/85] cleanup the old approach --- R/teal_lockfile.R | 61 ++++++++++------------------------------------- 1 file changed, 12 insertions(+), 49 deletions(-) diff --git a/R/teal_lockfile.R b/R/teal_lockfile.R index c922fd39b2..dd7c4c6500 100644 --- a/R/teal_lockfile.R +++ b/R/teal_lockfile.R @@ -39,52 +39,23 @@ teal_lockfile <- function() { } } - if (!(is_in_test() || is_r_cmd_check())) { - # old_plan <- future::plan() - # # If there is already a parallel (non-sequential) backend, reuse it. - # if (inherits(old_plan, "sequential")) { - # future::plan(future::multisession, workers = 2) - # } - # - # lockfile_task <- ExtendedTask$new(create_renv_lockfile) - # lockfile_task$invoke(close = inherits(old_plan, "sequential"), lockfile_path) - callr::r_bg(create_renv_lockfile, args = list(lockfile_path = lockfile_path)) - logger::log_trace("lockfile creation invoked.") - } + shiny::onStop(function() file.remove(lockfile_path)) + callr::r_bg(create_renv_lockfile, args = list(lockfile_path = lockfile_path)) + logger::log_trace("lockfile creation started.") } -create_renv_lockfile <- function(#close = FALSE, - lockfile_path = NULL) { - #checkmate::assert_flag(close) +create_renv_lockfile <- function(lockfile_path = NULL) { checkmate::assert_string(lockfile_path, na.ok = TRUE) - #promise <- promises::future_promise({ - # Below we can not use a file created in tempdir() directory. - # If a file is created in tempdir(), it gets deleted on 'then(onFulfilled' part. - shiny::onStop(function() file.remove(lockfile_path)) - renv_logs <- utils::capture.output( - renv::snapshot( - lockfile = lockfile_path, - prompt = FALSE, - force = TRUE - # type = is taken from renv::settings$snapshot.type() - ) + renv_logs <- utils::capture.output( + renv::snapshot( + lockfile = lockfile_path, + prompt = FALSE, + force = TRUE + # type = is taken from renv::settings$snapshot.type() ) - if (any(grepl("Lockfile written", renv_logs))) { - logger::log_trace("lockfile created successfully.") - } else { - logger::log_trace("lockfile created with issues.") - } - - lockfile_path - #}) - # if (close) { - # # If the initial backend was only sequential, bring it back. - # promises::then(promise, onFulfilled = function() { - # future::plan(future::sequential) - # }) - # } - # promise + ) + lockfile_path } teal_lockfile_downloadhandler <- function() { @@ -106,11 +77,3 @@ teal_lockfile_downloadhandler <- function() { contentType = "application/json" ) } - -is_r_cmd_check <- function() { - ("CheckExEnv" %in% search()) || any(c("_R_CHECK_TIMINGS_", "_R_CHECK_LICENSE_") %in% names(Sys.getenv())) -} - -is_in_test <- function() { - identical(Sys.getenv("TESTTHAT"), "true") -} From 5911bd3e59c7664708767f40162e984722249b90 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 1 Jul 2024 13:20:07 +0000 Subject: [PATCH 04/85] [skip style] [skip vbump] Restyle files --- tests/testthat/test-utils.R | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/testthat/test-utils.R b/tests/testthat/test-utils.R index cad55e8d0f..d7e8b58b9a 100644 --- a/tests/testthat/test-utils.R +++ b/tests/testthat/test-utils.R @@ -254,7 +254,6 @@ testthat::test_that("defunction recursively goes down a list", { }) testthat::test_that("create_renv_lockfile creates a lock file during the execution", { - renv_file_name <- "teal_app.lock" withr::defer(file.remove(renv_file_name)) teal:::create_renv_lockfile(renv_file_name) From e46337c1f3878d8ccc94a7e46f42d73aa8f48fe1 Mon Sep 17 00:00:00 2001 From: m7pr Date: Mon, 1 Jul 2024 16:19:04 +0200 Subject: [PATCH 05/85] do not assign renv output to anything --- R/teal_lockfile.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/teal_lockfile.R b/R/teal_lockfile.R index dd7c4c6500..45080abe0f 100644 --- a/R/teal_lockfile.R +++ b/R/teal_lockfile.R @@ -47,7 +47,7 @@ teal_lockfile <- function() { create_renv_lockfile <- function(lockfile_path = NULL) { checkmate::assert_string(lockfile_path, na.ok = TRUE) - renv_logs <- utils::capture.output( + utils::capture.output( renv::snapshot( lockfile = lockfile_path, prompt = FALSE, From f0ee2fd6465322240a5f1b088c5afcc84ad13b89 Mon Sep 17 00:00:00 2001 From: m7pr Date: Mon, 1 Jul 2024 16:21:02 +0200 Subject: [PATCH 06/85] no need to capture renv::snapshot output as nothing gets printed out of callr --- R/teal_lockfile.R | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/R/teal_lockfile.R b/R/teal_lockfile.R index 45080abe0f..3200c54fd0 100644 --- a/R/teal_lockfile.R +++ b/R/teal_lockfile.R @@ -47,13 +47,11 @@ teal_lockfile <- function() { create_renv_lockfile <- function(lockfile_path = NULL) { checkmate::assert_string(lockfile_path, na.ok = TRUE) - utils::capture.output( - renv::snapshot( - lockfile = lockfile_path, - prompt = FALSE, - force = TRUE - # type = is taken from renv::settings$snapshot.type() - ) + renv::snapshot( + lockfile = lockfile_path, + prompt = FALSE, + force = TRUE + # type = is taken from renv::settings$snapshot.type() ) lockfile_path } From 3650a9d76d7b073e7ef49502e31a62ccd1ba51fc Mon Sep 17 00:00:00 2001 From: m7pr Date: Tue, 2 Jul 2024 11:45:39 +0200 Subject: [PATCH 07/85] pass options and renv objects --- R/teal_lockfile.R | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/R/teal_lockfile.R b/R/teal_lockfile.R index 3200c54fd0..eddf61f92e 100644 --- a/R/teal_lockfile.R +++ b/R/teal_lockfile.R @@ -40,12 +40,15 @@ teal_lockfile <- function() { } shiny::onStop(function() file.remove(lockfile_path)) - callr::r_bg(create_renv_lockfile, args = list(lockfile_path = lockfile_path)) + callr::r_bg(create_renv_lockfile, args = list(lockfile_path = lockfile_path, opts = options()), package = 'renv') logger::log_trace("lockfile creation started.") } -create_renv_lockfile <- function(lockfile_path = NULL) { +create_renv_lockfile <- function(lockfile_path = NULL, opts) { checkmate::assert_string(lockfile_path, na.ok = TRUE) + checkmate::assert_list(opts) + + options(opts) renv::snapshot( lockfile = lockfile_path, From 0689000e7641232cc26a002c634080a67e5534f3 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 2 Jul 2024 09:47:34 +0000 Subject: [PATCH 08/85] [skip style] [skip vbump] Restyle files --- R/teal_lockfile.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/teal_lockfile.R b/R/teal_lockfile.R index eddf61f92e..ebf8f85cb6 100644 --- a/R/teal_lockfile.R +++ b/R/teal_lockfile.R @@ -40,7 +40,7 @@ teal_lockfile <- function() { } shiny::onStop(function() file.remove(lockfile_path)) - callr::r_bg(create_renv_lockfile, args = list(lockfile_path = lockfile_path, opts = options()), package = 'renv') + callr::r_bg(create_renv_lockfile, args = list(lockfile_path = lockfile_path, opts = options()), package = "renv") logger::log_trace("lockfile creation started.") } From b40417e5c69f9e5aa3bf2188d908b62c8f81134d Mon Sep 17 00:00:00 2001 From: Marcin <133694481+m7pr@users.noreply.github.com> Date: Tue, 2 Jul 2024 13:14:49 +0200 Subject: [PATCH 09/85] add comments Signed-off-by: Marcin <133694481+m7pr@users.noreply.github.com> --- R/teal_lockfile.R | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/R/teal_lockfile.R b/R/teal_lockfile.R index ebf8f85cb6..cc22245a97 100644 --- a/R/teal_lockfile.R +++ b/R/teal_lockfile.R @@ -40,7 +40,17 @@ teal_lockfile <- function() { } shiny::onStop(function() file.remove(lockfile_path)) - callr::r_bg(create_renv_lockfile, args = list(lockfile_path = lockfile_path, opts = options()), package = "renv") + callr::r_bg( + func = create_renv_lockfile, + args = list( + lockfile_path = lockfile_path, + opts = options() + ), + # renv setup is orchestrated by its special S3 objects: renv::settings, renv::config and renv::paths + # `package` parameter include `renv` namespace inside the environment of `func = create_renv_lockfile` function + package = "renv" + # default env = NULL # means that callr process will use environmental variables from the main session (parent process) + ) logger::log_trace("lockfile creation started.") } From f4b51d6496da8f1ef0d0d2300e85afbc41261599 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 2 Jul 2024 11:16:39 +0000 Subject: [PATCH 10/85] [skip style] [skip vbump] Restyle files --- R/teal_lockfile.R | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/R/teal_lockfile.R b/R/teal_lockfile.R index cc22245a97..3325286a0e 100644 --- a/R/teal_lockfile.R +++ b/R/teal_lockfile.R @@ -41,16 +41,16 @@ teal_lockfile <- function() { shiny::onStop(function() file.remove(lockfile_path)) callr::r_bg( - func = create_renv_lockfile, + func = create_renv_lockfile, args = list( - lockfile_path = lockfile_path, + lockfile_path = lockfile_path, opts = options() - ), + ), # renv setup is orchestrated by its special S3 objects: renv::settings, renv::config and renv::paths # `package` parameter include `renv` namespace inside the environment of `func = create_renv_lockfile` function - package = "renv" + package = "renv" # default env = NULL # means that callr process will use environmental variables from the main session (parent process) - ) + ) logger::log_trace("lockfile creation started.") } From e04fff129ce0088b9d28748ca4d4d543957c798b Mon Sep 17 00:00:00 2001 From: m7pr Date: Tue, 2 Jul 2024 14:18:11 +0200 Subject: [PATCH 11/85] bring back logging --- R/teal_lockfile.R | 50 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 35 insertions(+), 15 deletions(-) diff --git a/R/teal_lockfile.R b/R/teal_lockfile.R index 3325286a0e..6ec3291637 100644 --- a/R/teal_lockfile.R +++ b/R/teal_lockfile.R @@ -40,17 +40,30 @@ teal_lockfile <- function() { } shiny::onStop(function() file.remove(lockfile_path)) - callr::r_bg( - func = create_renv_lockfile, - args = list( - lockfile_path = lockfile_path, - opts = options() + + # Both capture.output are not needed if stdout = "|" + callr_output <- + capture.output( # Needed to suppress: 'Opening fd 1' message + capture.output( # Needed to suppress: 'PROCESS 'Rterm', running, pid' output + callr::r_bg( + func = create_renv_lockfile, + args = list( + lockfile_path = lockfile_path, + opts = options() + ), + # print results to the console + stdout = "", + # renv setup is orchestrated by its special S3 objects: renv::settings, renv::config and renv::paths + # `package` parameter include `renv` namespace inside the environment of `func = create_renv_lockfile` fun + package = "renv" + # default env = NULL # means that callr process will use environmental variables + # from the main session (parent process) + ), + type = "message" ), - # renv setup is orchestrated by its special S3 objects: renv::settings, renv::config and renv::paths - # `package` parameter include `renv` namespace inside the environment of `func = create_renv_lockfile` function - package = "renv" - # default env = NULL # means that callr process will use environmental variables from the main session (parent process) + type = "output" ) + logger::log_trace("lockfile creation started.") } @@ -60,13 +73,20 @@ create_renv_lockfile <- function(lockfile_path = NULL, opts) { options(opts) - renv::snapshot( - lockfile = lockfile_path, - prompt = FALSE, - force = TRUE - # type = is taken from renv::settings$snapshot.type() + renv_logs <- utils::capture.output( + renv::snapshot( + lockfile = lockfile_path, + prompt = FALSE, + force = TRUE + # type = is taken from renv::settings$snapshot.type() + ) ) - lockfile_path + # print() is required for callr::r_bg to track console output + if (any(grepl("Lockfile written", renv_logs))) { + print("lockfile created successfully.") + } else { + print("lockfile created with issues.") + } } teal_lockfile_downloadhandler <- function() { From 13d965a7f3bef3546ee9db4727c8e763eac6bb27 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 2 Jul 2024 12:21:11 +0000 Subject: [PATCH 12/85] [skip style] [skip vbump] Restyle files --- R/teal_lockfile.R | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/R/teal_lockfile.R b/R/teal_lockfile.R index 6ec3291637..d3ab63395a 100644 --- a/R/teal_lockfile.R +++ b/R/teal_lockfile.R @@ -43,26 +43,26 @@ teal_lockfile <- function() { # Both capture.output are not needed if stdout = "|" callr_output <- - capture.output( # Needed to suppress: 'Opening fd 1' message - capture.output( # Needed to suppress: 'PROCESS 'Rterm', running, pid' output - callr::r_bg( - func = create_renv_lockfile, - args = list( - lockfile_path = lockfile_path, - opts = options() + capture.output( # Needed to suppress: 'Opening fd 1' message + capture.output( # Needed to suppress: 'PROCESS 'Rterm', running, pid' output + callr::r_bg( + func = create_renv_lockfile, + args = list( + lockfile_path = lockfile_path, + opts = options() + ), + # print results to the console + stdout = "", + # renv setup is orchestrated by its special S3 objects: renv::settings, renv::config and renv::paths + # `package` parameter include `renv` namespace inside the environment of `func = create_renv_lockfile` fun + package = "renv" + # default env = NULL # means that callr process will use environmental variables + # from the main session (parent process) ), - # print results to the console - stdout = "", - # renv setup is orchestrated by its special S3 objects: renv::settings, renv::config and renv::paths - # `package` parameter include `renv` namespace inside the environment of `func = create_renv_lockfile` fun - package = "renv" - # default env = NULL # means that callr process will use environmental variables - # from the main session (parent process) + type = "message" ), - type = "message" - ), - type = "output" - ) + type = "output" + ) logger::log_trace("lockfile creation started.") } From 23df7ca23fde23f74d1c2c1ff29a6163ee8b1243 Mon Sep 17 00:00:00 2001 From: m7pr Date: Tue, 2 Jul 2024 14:21:58 +0200 Subject: [PATCH 13/85] prefixes --- R/teal_lockfile.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/R/teal_lockfile.R b/R/teal_lockfile.R index 6ec3291637..3ddc5a9df5 100644 --- a/R/teal_lockfile.R +++ b/R/teal_lockfile.R @@ -43,8 +43,8 @@ teal_lockfile <- function() { # Both capture.output are not needed if stdout = "|" callr_output <- - capture.output( # Needed to suppress: 'Opening fd 1' message - capture.output( # Needed to suppress: 'PROCESS 'Rterm', running, pid' output + utils::capture.output( # Needed to suppress: 'Opening fd 1' message + utils::capture.output( # Needed to suppress: 'PROCESS 'Rterm', running, pid' output callr::r_bg( func = create_renv_lockfile, args = list( From b71755847eaaae59bc6cbdf310cffda91f2a1985 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 2 Jul 2024 12:26:29 +0000 Subject: [PATCH 14/85] [skip style] [skip vbump] Restyle files --- R/teal_lockfile.R | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/R/teal_lockfile.R b/R/teal_lockfile.R index 36a66e3e5b..7613adbb10 100644 --- a/R/teal_lockfile.R +++ b/R/teal_lockfile.R @@ -43,21 +43,21 @@ teal_lockfile <- function() { # Both capture.output are not needed if stdout = "|" callr_output <- - utils::capture.output( # Needed to suppress: 'Opening fd 1' message - utils::capture.output( # Needed to suppress: 'PROCESS 'Rterm', running, pid' output - callr::r_bg( - func = create_renv_lockfile, - args = list( - lockfile_path = lockfile_path, - opts = options() - ), - # print results to the console - stdout = "", - # renv setup is orchestrated by its special S3 objects: renv::settings, renv::config and renv::paths - # `package` parameter include `renv` namespace inside the environment of `func = create_renv_lockfile` fun - package = "renv" - # default env = NULL # means that callr process will use environmental variables - # from the main session (parent process) + utils::capture.output( # Needed to suppress: 'Opening fd 1' message + utils::capture.output( # Needed to suppress: 'PROCESS 'Rterm', running, pid' output + callr::r_bg( + func = create_renv_lockfile, + args = list( + lockfile_path = lockfile_path, + opts = options() + ), + # print results to the console + stdout = "", + # renv setup is orchestrated by its special S3 objects: renv::settings, renv::config and renv::paths + # `package` parameter include `renv` namespace inside the environment of `func = create_renv_lockfile` fun + package = "renv" + # default env = NULL # means that callr process will use environmental variables + # from the main session (parent process) ), type = "message" ), From 9208909b115971f51f188f047beb7a8e18050828 Mon Sep 17 00:00:00 2001 From: Marcin <133694481+m7pr@users.noreply.github.com> Date: Tue, 2 Jul 2024 14:56:36 +0200 Subject: [PATCH 15/85] Update R/teal_lockfile.R Co-authored-by: Pawel Rucki <12943682+pawelru@users.noreply.github.com> Signed-off-by: Marcin <133694481+m7pr@users.noreply.github.com> --- R/teal_lockfile.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/teal_lockfile.R b/R/teal_lockfile.R index 7613adbb10..cae6f1220e 100644 --- a/R/teal_lockfile.R +++ b/R/teal_lockfile.R @@ -67,7 +67,7 @@ teal_lockfile <- function() { logger::log_trace("lockfile creation started.") } -create_renv_lockfile <- function(lockfile_path = NULL, opts) { +create_renv_lockfile <- function(lockfile_path = NULL, opts = NULL) { checkmate::assert_string(lockfile_path, na.ok = TRUE) checkmate::assert_list(opts) From f7fbc87d0b886635ff74ba366bb3a00ebe5e3504 Mon Sep 17 00:00:00 2001 From: Marcin <133694481+m7pr@users.noreply.github.com> Date: Tue, 2 Jul 2024 15:03:52 +0200 Subject: [PATCH 16/85] Update tests/testthat/test-utils.R Co-authored-by: Pawel Rucki <12943682+pawelru@users.noreply.github.com> Signed-off-by: Marcin <133694481+m7pr@users.noreply.github.com> --- tests/testthat/test-utils.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/testthat/test-utils.R b/tests/testthat/test-utils.R index d7e8b58b9a..78b1730565 100644 --- a/tests/testthat/test-utils.R +++ b/tests/testthat/test-utils.R @@ -256,7 +256,7 @@ testthat::test_that("defunction recursively goes down a list", { testthat::test_that("create_renv_lockfile creates a lock file during the execution", { renv_file_name <- "teal_app.lock" withr::defer(file.remove(renv_file_name)) - teal:::create_renv_lockfile(renv_file_name) + create_renv_lockfile(renv_file_name) testthat::expect_true(file.exists(renv_file_name)) }) From d4005709149db97ccc74455e160972b24f0c7b6b Mon Sep 17 00:00:00 2001 From: m7pr Date: Wed, 3 Jul 2024 12:09:06 +0200 Subject: [PATCH 17/85] test for a lockfile in init --- tests/testthat/test-init.R | 17 +++++++++++++++++ tests/testthat/test-utils.R | 8 -------- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/tests/testthat/test-init.R b/tests/testthat/test-init.R index 0392763b5f..f75b9be31b 100644 --- a/tests/testthat/test-init.R +++ b/tests/testthat/test-init.R @@ -77,3 +77,20 @@ testthat::test_that("init throws when dataname in filter incompatible w/ datanam "Filter 'iris Species' refers to dataname not available in 'data'" ) }) + +testthat::test_that("init creates a lockfile during the execution", { + renv_file_name <- "teal_app.lock" + withr::defer(file.remove(renv_file_name)) + app <- init( + data = teal.data::teal_data(iris = iris), + modules = example_module(label = "example teal module") + ) + + iter <- 1 + while (!file.exists(renv_file_name) && iter <= 100) { + Sys.sleep(0.25) + iter <- iter + 1 # max wait time is 25 seconds + } + + testthat::expect_true(file.exists(renv_file_name)) +}) diff --git a/tests/testthat/test-utils.R b/tests/testthat/test-utils.R index 78b1730565..67a0628c95 100644 --- a/tests/testthat/test-utils.R +++ b/tests/testthat/test-utils.R @@ -252,11 +252,3 @@ testthat::test_that("defunction recursively goes down a list", { y ) }) - -testthat::test_that("create_renv_lockfile creates a lock file during the execution", { - renv_file_name <- "teal_app.lock" - withr::defer(file.remove(renv_file_name)) - create_renv_lockfile(renv_file_name) - - testthat::expect_true(file.exists(renv_file_name)) -}) From e7c7c7b5fbea4f8850e07f3d1c445e4a77a348d5 Mon Sep 17 00:00:00 2001 From: m7pr Date: Wed, 3 Jul 2024 14:04:10 +0200 Subject: [PATCH 18/85] less capture.output is needed --- R/teal_lockfile.R | 38 +++++++++++++++++--------------------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/R/teal_lockfile.R b/R/teal_lockfile.R index cae6f1220e..a425f7a514 100644 --- a/R/teal_lockfile.R +++ b/R/teal_lockfile.R @@ -41,28 +41,24 @@ teal_lockfile <- function() { shiny::onStop(function() file.remove(lockfile_path)) - # Both capture.output are not needed if stdout = "|" - callr_output <- - utils::capture.output( # Needed to suppress: 'Opening fd 1' message - utils::capture.output( # Needed to suppress: 'PROCESS 'Rterm', running, pid' output - callr::r_bg( - func = create_renv_lockfile, - args = list( - lockfile_path = lockfile_path, - opts = options() - ), - # print results to the console - stdout = "", - # renv setup is orchestrated by its special S3 objects: renv::settings, renv::config and renv::paths - # `package` parameter include `renv` namespace inside the environment of `func = create_renv_lockfile` fun - package = "renv" - # default env = NULL # means that callr process will use environmental variables - # from the main session (parent process) - ), - type = "message" + # capture.output is not needed if stdout = "|" + utils::capture.output( # Needed to suppress: 'Opening fd 1' message + callr::r_bg( + func = create_renv_lockfile, + args = list( + lockfile_path = lockfile_path, + opts = options() ), - type = "output" - ) + # print results to the console + stdout = "", + # renv setup is orchestrated by its special S3 objects: renv::settings, renv::config and renv::paths + # `package` parameter include `renv` namespace inside the environment of `func = create_renv_lockfile` fun + package = "renv" + # default env = NULL # means that callr process will use environmental variables + # from the main session (parent process) + ), + type = "message" + ) logger::log_trace("lockfile creation started.") } From 6b1c0df0147d8374443c2f96ae399854cf29c0c3 Mon Sep 17 00:00:00 2001 From: m7pr Date: Wed, 3 Jul 2024 14:06:22 +0200 Subject: [PATCH 19/85] expose env = NULL and unify documentation --- R/teal_lockfile.R | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/R/teal_lockfile.R b/R/teal_lockfile.R index a425f7a514..d7f9614b17 100644 --- a/R/teal_lockfile.R +++ b/R/teal_lockfile.R @@ -49,13 +49,13 @@ teal_lockfile <- function() { lockfile_path = lockfile_path, opts = options() ), - # print results to the console + # prints results to the console stdout = "", # renv setup is orchestrated by its special S3 objects: renv::settings, renv::config and renv::paths - # `package` parameter include `renv` namespace inside the environment of `func = create_renv_lockfile` fun - package = "renv" - # default env = NULL # means that callr process will use environmental variables - # from the main session (parent process) + # `package` parameter include `renv` namespace inside the environment of `func = create_renv_lockfile` + package = "renv", + # callr process will use environmental variables from the main session (parent process) + env = NULL ), type = "message" ) From 0114333a575f455893aef3e88e11e45b5bc005d3 Mon Sep 17 00:00:00 2001 From: m7pr Date: Wed, 3 Jul 2024 14:55:34 +0200 Subject: [PATCH 20/85] bring create_renv_lockfile_2 for testing --- R/teal_lockfile.R | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/R/teal_lockfile.R b/R/teal_lockfile.R index d7f9614b17..56073a44a2 100644 --- a/R/teal_lockfile.R +++ b/R/teal_lockfile.R @@ -41,10 +41,10 @@ teal_lockfile <- function() { shiny::onStop(function() file.remove(lockfile_path)) - # capture.output is not needed if stdout = "|" - utils::capture.output( # Needed to suppress: 'Opening fd 1' message + # # capture.output is not needed if stdout = "|" + # utils::capture.output( # Needed to suppress: 'Opening fd 1' message callr::r_bg( - func = create_renv_lockfile, + func = create_renv_lockfile_2, args = list( lockfile_path = lockfile_path, opts = options() @@ -55,14 +55,18 @@ teal_lockfile <- function() { # `package` parameter include `renv` namespace inside the environment of `func = create_renv_lockfile` package = "renv", # callr process will use environmental variables from the main session (parent process) - env = NULL - ), - type = "message" - ) + env = Sys.getenv() + )#, + # type = "message" + # ) logger::log_trace("lockfile creation started.") } +create_renv_lockfile_2 <- function(lockfile_path = NULL, opts = NULL) { + lapply(1:15, function(x) {print(x); Sys.sleep(1)}) +} + create_renv_lockfile <- function(lockfile_path = NULL, opts = NULL) { checkmate::assert_string(lockfile_path, na.ok = TRUE) checkmate::assert_list(opts) From 58a9e8d9cddc992c586b04d8087b7c091844535c Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 3 Jul 2024 12:57:58 +0000 Subject: [PATCH 21/85] [skip style] [skip vbump] Restyle files --- R/teal_lockfile.R | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/R/teal_lockfile.R b/R/teal_lockfile.R index 56073a44a2..5eed2bc757 100644 --- a/R/teal_lockfile.R +++ b/R/teal_lockfile.R @@ -43,20 +43,20 @@ teal_lockfile <- function() { # # capture.output is not needed if stdout = "|" # utils::capture.output( # Needed to suppress: 'Opening fd 1' message - callr::r_bg( - func = create_renv_lockfile_2, - args = list( - lockfile_path = lockfile_path, - opts = options() - ), - # prints results to the console - stdout = "", - # renv setup is orchestrated by its special S3 objects: renv::settings, renv::config and renv::paths - # `package` parameter include `renv` namespace inside the environment of `func = create_renv_lockfile` - package = "renv", - # callr process will use environmental variables from the main session (parent process) - env = Sys.getenv() - )#, + callr::r_bg( + func = create_renv_lockfile_2, + args = list( + lockfile_path = lockfile_path, + opts = options() + ), + # prints results to the console + stdout = "", + # renv setup is orchestrated by its special S3 objects: renv::settings, renv::config and renv::paths + # `package` parameter include `renv` namespace inside the environment of `func = create_renv_lockfile` + package = "renv", + # callr process will use environmental variables from the main session (parent process) + env = Sys.getenv() + ) # , # type = "message" # ) @@ -64,7 +64,10 @@ teal_lockfile <- function() { } create_renv_lockfile_2 <- function(lockfile_path = NULL, opts = NULL) { - lapply(1:15, function(x) {print(x); Sys.sleep(1)}) + lapply(1:15, function(x) { + print(x) + Sys.sleep(1) + }) } create_renv_lockfile <- function(lockfile_path = NULL, opts = NULL) { From 2632e5c452c1851ca162afe3ae2a35a2cb641bd1 Mon Sep 17 00:00:00 2001 From: m7pr Date: Wed, 3 Jul 2024 14:58:09 +0200 Subject: [PATCH 22/85] append the name to the counter for the test function --- R/teal_lockfile.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/teal_lockfile.R b/R/teal_lockfile.R index 56073a44a2..e9a9237cae 100644 --- a/R/teal_lockfile.R +++ b/R/teal_lockfile.R @@ -64,7 +64,7 @@ teal_lockfile <- function() { } create_renv_lockfile_2 <- function(lockfile_path = NULL, opts = NULL) { - lapply(1:15, function(x) {print(x); Sys.sleep(1)}) + lapply(1:15, function(x) {print(paste0('teal:', x)); Sys.sleep(1)}) } create_renv_lockfile <- function(lockfile_path = NULL, opts = NULL) { From 027faee48cda8a1c067a0b59d195fb05385f0472 Mon Sep 17 00:00:00 2001 From: m7pr Date: Thu, 4 Jul 2024 10:40:09 +0200 Subject: [PATCH 23/85] store r_bg process to a variable to prevent from killing the process --- R/init.R | 2 +- R/teal_lockfile.R | 12 +++--------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/R/init.R b/R/init.R index 5aca99e100..39e780b079 100644 --- a/R/init.R +++ b/R/init.R @@ -154,7 +154,7 @@ init <- function(data, teal.logger::log_system_info() # invoke lockfile creation - teal_lockfile() + process <- teal_lockfile() # argument transformations ## `modules` - landing module diff --git a/R/teal_lockfile.R b/R/teal_lockfile.R index 80cdf5cafc..09aecfb8f4 100644 --- a/R/teal_lockfile.R +++ b/R/teal_lockfile.R @@ -43,8 +43,8 @@ teal_lockfile <- function() { # # capture.output is not needed if stdout = "|" # utils::capture.output( # Needed to suppress: 'Opening fd 1' message - callr::r_bg( - func = create_renv_lockfile_2, + process <- callr::r_bg( + func = create_renv_lockfile, args = list( lockfile_path = lockfile_path, opts = options() @@ -61,13 +61,7 @@ teal_lockfile <- function() { # ) logger::log_trace("lockfile creation started.") -} - -create_renv_lockfile_2 <- function(lockfile_path = NULL, opts = NULL) { - lapply(1:15, function(x) { - print(paste0('teal:', x))) - Sys.sleep(1) - }) + process } create_renv_lockfile <- function(lockfile_path = NULL, opts = NULL) { From 4467cd2fcd41e68f93962f66d0d37d83c0005992 Mon Sep 17 00:00:00 2001 From: m7pr Date: Thu, 4 Jul 2024 13:07:07 +0200 Subject: [PATCH 24/85] prototype of the reactivePoll --- R/init.R | 2 ++ R/teal_lockfile.R | 46 ++++++++++++++++++++++++++-------------------- 2 files changed, 28 insertions(+), 20 deletions(-) diff --git a/R/init.R b/R/init.R index 39e780b079..6754a1bd77 100644 --- a/R/init.R +++ b/R/init.R @@ -231,6 +231,8 @@ init <- function(data, if (!is.null(landing_module)) { do.call(landing_module$server, c(list(id = "landing_module_shiny_id"), landing_module$server_args)) } + session$userData$lockfile_process <- process + reactivePoll(1000, session, checkFunc = lockfile_status, valueFunc = function(){}) srv_teal_with_splash(id = id, data = data, modules = modules, filter = deep_copy_filter(filter)) } ) diff --git a/R/teal_lockfile.R b/R/teal_lockfile.R index 09aecfb8f4..e02cd70c3e 100644 --- a/R/teal_lockfile.R +++ b/R/teal_lockfile.R @@ -41,24 +41,18 @@ teal_lockfile <- function() { shiny::onStop(function() file.remove(lockfile_path)) - # # capture.output is not needed if stdout = "|" - # utils::capture.output( # Needed to suppress: 'Opening fd 1' message - process <- callr::r_bg( + process <- callr::r_bg( # callr process needs to be assigned to an object so does shiny doesn't kill it on shinyApp func = create_renv_lockfile, args = list( lockfile_path = lockfile_path, opts = options() ), - # prints results to the console - stdout = "", # renv setup is orchestrated by its special S3 objects: renv::settings, renv::config and renv::paths # `package` parameter include `renv` namespace inside the environment of `func = create_renv_lockfile` package = "renv", # callr process will use environmental variables from the main session (parent process) env = Sys.getenv() - ) # , - # type = "message" - # ) + ) logger::log_trace("lockfile creation started.") process @@ -70,20 +64,16 @@ create_renv_lockfile <- function(lockfile_path = NULL, opts = NULL) { options(opts) - renv_logs <- utils::capture.output( - renv::snapshot( - lockfile = lockfile_path, - prompt = FALSE, - force = TRUE - # type = is taken from renv::settings$snapshot.type() + print( # print() is required for callr::r_bg to track output with $read_output() + utils::capture.output( + renv::snapshot( + lockfile = lockfile_path, + prompt = FALSE, + force = TRUE + # type = is taken from renv::settings$snapshot.type() + ) ) ) - # print() is required for callr::r_bg to track console output - if (any(grepl("Lockfile written", renv_logs))) { - print("lockfile created successfully.") - } else { - print("lockfile created with issues.") - } } teal_lockfile_downloadhandler <- function() { @@ -105,3 +95,19 @@ teal_lockfile_downloadhandler <- function() { contentType = "application/json" ) } + +lockfile_status <- function() { + process <- session$userData$lockfile_process + if (!process$is_alive()) { + renv_logs <- process$read_output() + + if (any(grepl("Lockfile written", renv_logs))) { + logger::log_trace("lockfile created successfully.") + } else { + logger::log_trace("lockfile created with issues.") + } + TRUE + } else { + FALSE + } +} From 86403e88251cc64668263c92a185eec98d3a4ce6 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 4 Jul 2024 11:09:15 +0000 Subject: [PATCH 25/85] [skip style] [skip vbump] Restyle files --- R/init.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/init.R b/R/init.R index 6754a1bd77..144a081f0c 100644 --- a/R/init.R +++ b/R/init.R @@ -232,7 +232,7 @@ init <- function(data, do.call(landing_module$server, c(list(id = "landing_module_shiny_id"), landing_module$server_args)) } session$userData$lockfile_process <- process - reactivePoll(1000, session, checkFunc = lockfile_status, valueFunc = function(){}) + reactivePoll(1000, session, checkFunc = lockfile_status, valueFunc = function() {}) srv_teal_with_splash(id = id, data = data, modules = modules, filter = deep_copy_filter(filter)) } ) From d6a82e458589d2eb027d6da7c734ca4827e3231e Mon Sep 17 00:00:00 2001 From: m7pr Date: Wed, 10 Jul 2024 14:09:02 +0200 Subject: [PATCH 26/85] styler check --- R/init.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/init.R b/R/init.R index 6754a1bd77..144a081f0c 100644 --- a/R/init.R +++ b/R/init.R @@ -232,7 +232,7 @@ init <- function(data, do.call(landing_module$server, c(list(id = "landing_module_shiny_id"), landing_module$server_args)) } session$userData$lockfile_process <- process - reactivePoll(1000, session, checkFunc = lockfile_status, valueFunc = function(){}) + reactivePoll(1000, session, checkFunc = lockfile_status, valueFunc = function() {}) srv_teal_with_splash(id = id, data = data, modules = modules, filter = deep_copy_filter(filter)) } ) From 3c480a7be0524dbcaa30f2a2d6165069df813e9c Mon Sep 17 00:00:00 2001 From: m7pr Date: Wed, 10 Jul 2024 14:57:37 +0200 Subject: [PATCH 27/85] lockfile_status_tracker --- R/init.R | 3 +-- R/teal_lockfile.R | 29 +++++++++++++++++------------ 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/R/init.R b/R/init.R index 144a081f0c..ec51fde04f 100644 --- a/R/init.R +++ b/R/init.R @@ -231,8 +231,7 @@ init <- function(data, if (!is.null(landing_module)) { do.call(landing_module$server, c(list(id = "landing_module_shiny_id"), landing_module$server_args)) } - session$userData$lockfile_process <- process - reactivePoll(1000, session, checkFunc = lockfile_status, valueFunc = function() {}) + lockfile_status_tracker(process) srv_teal_with_splash(id = id, data = data, modules = modules, filter = deep_copy_filter(filter)) } ) diff --git a/R/teal_lockfile.R b/R/teal_lockfile.R index e02cd70c3e..89e98d6361 100644 --- a/R/teal_lockfile.R +++ b/R/teal_lockfile.R @@ -96,18 +96,23 @@ teal_lockfile_downloadhandler <- function() { ) } -lockfile_status <- function() { - process <- session$userData$lockfile_process - if (!process$is_alive()) { - renv_logs <- process$read_output() - - if (any(grepl("Lockfile written", renv_logs))) { - logger::log_trace("lockfile created successfully.") - } else { - logger::log_trace("lockfile created with issues.") - } - TRUE +lockfile_status <- function(process) { + renv_logs <- process$read_output() + if (any(grepl("Lockfile written", renv_logs))) { + logger::log_trace("lockfile created successfully.") } else { - FALSE + logger::log_trace("lockfile created with issues.") } } + +lockfile_status_tracker <- function(process) { + timer <- reactiveTimer(1000) + + tracker <- observe({ + timer() + if (!process$is_alive()) { + lockfile_status(process) + tracker$destroy() + } + }) +} From d2b0d8b76c3e822b49021b62c697a0147ac1b5a3 Mon Sep 17 00:00:00 2001 From: m7pr Date: Thu, 11 Jul 2024 14:17:19 +0200 Subject: [PATCH 28/85] use showNotification to publish information about lofkcile creation --- R/teal_lockfile.R | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/R/teal_lockfile.R b/R/teal_lockfile.R index 89e98d6361..91532e92e7 100644 --- a/R/teal_lockfile.R +++ b/R/teal_lockfile.R @@ -98,11 +98,13 @@ teal_lockfile_downloadhandler <- function() { lockfile_status <- function(process) { renv_logs <- process$read_output() - if (any(grepl("Lockfile written", renv_logs))) { - logger::log_trace("lockfile created successfully.") - } else { - logger::log_trace("lockfile created with issues.") - } + message <- ifelse( + any(grepl("Lockfile written", renv_logs)), + "lockfile created successfully.", + "lockfile created with issues." + ) + logger::log_trace(message) + shiny::showNotification(message) } lockfile_status_tracker <- function(process) { From 6010c4fb1d08b9de8c4c52afafb398e66421570f Mon Sep 17 00:00:00 2001 From: m7pr Date: Thu, 11 Jul 2024 14:41:03 +0200 Subject: [PATCH 29/85] start lockfile download handler as hidden, and only display when lockfile creation is finished --- R/module_teal.R | 2 +- R/teal_lockfile.R | 7 +------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/R/module_teal.R b/R/module_teal.R index 43e0ccc37e..43bfcd4e91 100644 --- a/R/module_teal.R +++ b/R/module_teal.R @@ -112,7 +112,7 @@ ui_teal <- function(id, footer, teal.widgets::verbatim_popup_ui(ns("sessionInfo"), "Session Info", type = "link"), br(), - downloadLink(ns("lockFile"), "Download .lock file"), + shinyjs::hidden(downloadLink(ns("lockFile"), "Download .lock file")), textOutput(ns("identifier")) ) ) diff --git a/R/teal_lockfile.R b/R/teal_lockfile.R index 91532e92e7..a7476ad5ef 100644 --- a/R/teal_lockfile.R +++ b/R/teal_lockfile.R @@ -83,12 +83,6 @@ teal_lockfile_downloadhandler <- function() { }, content = function(file) { teal_lockfile <- "teal_app.lock" - iter <- 1 - while (!file.exists(teal_lockfile) && iter <= 100) { - logger::log_trace("lockfile not created yet, retrying...") - Sys.sleep(0.25) - iter <- iter + 1 # max wait time is 25 seconds - } file.copy(teal_lockfile, file) file }, @@ -105,6 +99,7 @@ lockfile_status <- function(process) { ) logger::log_trace(message) shiny::showNotification(message) + shinyjs::show("teal-lockFile") } lockfile_status_tracker <- function(process) { From 209538ec4ce11419701eea21da05a37e63cd0266 Mon Sep 17 00:00:00 2001 From: go_gonzo Date: Thu, 11 Jul 2024 18:31:54 +0200 Subject: [PATCH 30/85] assert --- R/teal_lockfile.R | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/R/teal_lockfile.R b/R/teal_lockfile.R index a7476ad5ef..4cb310643c 100644 --- a/R/teal_lockfile.R +++ b/R/teal_lockfile.R @@ -99,10 +99,11 @@ lockfile_status <- function(process) { ) logger::log_trace(message) shiny::showNotification(message) - shinyjs::show("teal-lockFile") + shinyjs::show(selector = "#teal-lockFile") } lockfile_status_tracker <- function(process) { + checkmate::assert_class(process, "r_process") timer <- reactiveTimer(1000) tracker <- observe({ From 82d104843aca7b9971a2d3673e2e501a67632448 Mon Sep 17 00:00:00 2001 From: go_gonzo Date: Fri, 12 Jul 2024 13:41:10 +0200 Subject: [PATCH 31/85] WIP tests --- DESCRIPTION | 4 +- R/teal_lockfile.R | 124 ++++++++++++++++++++++++++++++---------------- 2 files changed, 83 insertions(+), 45 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 2fe770b5c0..e22e6ec4d7 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -40,7 +40,7 @@ Depends: teal.data (>= 0.5.0), teal.slice (>= 0.5.0) Imports: - callr (>= 3.7.6), + mirai, checkmate (>= 2.1.0), jsonlite, lifecycle (>= 0.2.0), @@ -73,7 +73,7 @@ VignetteBuilder: RdMacros: lifecycle Config/Needs/verdepcheck: rstudio/shiny, insightsengineering/teal.data, - insightsengineering/teal.slice, r-lib/callr, mllg/checkmate, + insightsengineering/teal.slice, shikokuchuo/mirai, mllg/checkmate, jeroen/jsonlite, r-lib/lifecycle, daroczig/logger, tidyverse/magrittr, rstudio/renv, r-lib/rlang, daattali/shinyjs, insightsengineering/teal.logger, diff --git a/R/teal_lockfile.R b/R/teal_lockfile.R index 4cb310643c..3d04d5ae09 100644 --- a/R/teal_lockfile.R +++ b/R/teal_lockfile.R @@ -1,7 +1,18 @@ -#' Generate lockfile for application reproducibility +#' Generate lockfile for application's environment reproducibility #' -#' This function is invoked during [teal::init] to create `renv`-compatible lockfile for use within the application. +#' @section lockfile creation steps: +#' Process is split into multiple steps. #' +#' 1. `teal_lockfile` is executed in [init] before application starts. It is better when process starts in +#' [init] as it is a one-time process and does not need to be repeated when each `shiny` session starts. +#' Function invokes `ExtendedTask` with `create_renv_lockfile` to begin creation of the lockfile. +#' 2. `ExtendedTask` with background `mirai` process is passed to the `lockfile_status_handler` to track the +#' status of the task. +#' 3. Once `ExtendedTask` is completed, `lockfile_status_handler` is triggered to log the status of the lockfile, +#' send a notification to UI and show the download button. +#' 4. `teal_lockfile_downloadhandler` is used to download the lockfile (when button is shown). +#' +#' @section Different ways of creating lockfile: #' The function leverages [renv::snapshot()], which offers multiple methods for lockfile creation. #' #' - User-specified: @@ -33,6 +44,7 @@ teal_lockfile <- function() { if (!identical(user_lockfile, "")) { if (file.exists(user_lockfile)) { file.copy(user_lockfile, lockfile_path) + logger::log_trace('Lockfile set using option "teal.renv.lockfile" - skipping automatic creation.') return(invisible(NULL)) } else { stop("lockfile provided through options('teal.renv.lockfile') does not exist.") @@ -40,42 +52,54 @@ teal_lockfile <- function() { } shiny::onStop(function() file.remove(lockfile_path)) + process <- ExtendedTask$new(function(...) { + mirai::mirai( + run(lockfile_path = lockfile_path, opts = opts, sysenv = sysenv, libpaths = libpaths), + ... + ) + }) - process <- callr::r_bg( # callr process needs to be assigned to an object so does shiny doesn't kill it on shinyApp - func = create_renv_lockfile, - args = list( - lockfile_path = lockfile_path, - opts = options() - ), - # renv setup is orchestrated by its special S3 objects: renv::settings, renv::config and renv::paths - # `package` parameter include `renv` namespace inside the environment of `func = create_renv_lockfile` - package = "renv", - # callr process will use environmental variables from the main session (parent process) - env = Sys.getenv() + process$invoke( + lockfile_path = lockfile_path, + run = create_renv_lockfile, + opts = options(), + libpaths = .libPaths(), + sysenv = as.list(Sys.getenv()) # normally output is a class of "Dlist" ) + logger::log_trace("Lockfile creation started based on { getwd() }.") - logger::log_trace("lockfile creation started.") process } -create_renv_lockfile <- function(lockfile_path = NULL, opts = NULL) { - checkmate::assert_string(lockfile_path, na.ok = TRUE) +#' @rdname teal_lockfile +#' @keywords internal +create_renv_lockfile <- function(lockfile_path = NULL, opts, sysenv, libpaths) { + checkmate::assert_string(lockfile_path) checkmate::assert_list(opts) - + checkmate::assert_character(libpaths, min.len = 1) + checkmate::assert_class(sysenv, "list") options(opts) + lapply(names(sysenv), function(sysvar) do.call(Sys.setenv, sysenv[sysvar])) + .libPaths(libpaths) - print( # print() is required for callr::r_bg to track output with $read_output() - utils::capture.output( - renv::snapshot( - lockfile = lockfile_path, - prompt = FALSE, - force = TRUE - # type = is taken from renv::settings$snapshot.type() - ) + out <- capture.output( + renv <- renv::snapshot( + lockfile = lockfile_path, + prompt = FALSE, + force = TRUE + # type = is taken from renv::settings$snapshot.type() ) ) + + list( + out = out, + lockfile_path = lockfile_path, + length = length(renv$Packages) + ) } +#' @rdname teal_lockfile +#' @keywords internal teal_lockfile_downloadhandler <- function() { downloadHandler( filename = function() { @@ -90,27 +114,41 @@ teal_lockfile_downloadhandler <- function() { ) } -lockfile_status <- function(process) { - renv_logs <- process$read_output() - message <- ifelse( - any(grepl("Lockfile written", renv_logs)), - "lockfile created successfully.", - "lockfile created with issues." - ) +#' @rdname teal_lockfile +#' @keywords internal +lockfile_status_handler <- function(process) { + renv_logs <- process$result() + message <- if (any(grepl("Lockfile written to", renv_logs$out))) { + with <- if (any(grepl("WARNING:", renv_logs$out))) { + "with warning(s)" + } else if (any(grepl("ERROR:", renv_logs$out))) { + "with error(s)" + } else { + "" + } + + message <- sprintf( + "Lockfile %s containing %s packages saved %s.", + renv_logs$lockfile_path, + renv_logs$length, + with + ) + shinyjs::show(selector = "#teal-lockFile") + } else { + "Lockfile creation failed." + } logger::log_trace(message) shiny::showNotification(message) - shinyjs::show(selector = "#teal-lockFile") } +#' @rdname teal_lockfile +#' @keywords internal lockfile_status_tracker <- function(process) { - checkmate::assert_class(process, "r_process") - timer <- reactiveTimer(1000) - - tracker <- observe({ - timer() - if (!process$is_alive()) { - lockfile_status(process) - tracker$destroy() - } - }) + if (!is.null(isolate(process))) { + tracker <- observeEvent(process$status(), { + if (process$status() != "running") { + lockfile_status_handler(process) + } + }) + } } From f5cc36be3a38d14500c75878bea13ca9a6b0a1a7 Mon Sep 17 00:00:00 2001 From: go_gonzo Date: Fri, 12 Jul 2024 13:46:53 +0200 Subject: [PATCH 32/85] setwd --- R/teal_lockfile.R | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/R/teal_lockfile.R b/R/teal_lockfile.R index 3d04d5ae09..125f9f656b 100644 --- a/R/teal_lockfile.R +++ b/R/teal_lockfile.R @@ -54,7 +54,7 @@ teal_lockfile <- function() { shiny::onStop(function() file.remove(lockfile_path)) process <- ExtendedTask$new(function(...) { mirai::mirai( - run(lockfile_path = lockfile_path, opts = opts, sysenv = sysenv, libpaths = libpaths), + run(lockfile_path = lockfile_path, opts = opts, sysenv = sysenv, libpaths = libpaths, wd = wd), ... ) }) @@ -64,7 +64,8 @@ teal_lockfile <- function() { run = create_renv_lockfile, opts = options(), libpaths = .libPaths(), - sysenv = as.list(Sys.getenv()) # normally output is a class of "Dlist" + sysenv = as.list(Sys.getenv()) # normally output is a class of "Dlist", + wd = getwd() ) logger::log_trace("Lockfile creation started based on { getwd() }.") @@ -73,14 +74,17 @@ teal_lockfile <- function() { #' @rdname teal_lockfile #' @keywords internal -create_renv_lockfile <- function(lockfile_path = NULL, opts, sysenv, libpaths) { +create_renv_lockfile <- function(lockfile_path = NULL, opts, sysenv, libpaths, wd) { + # todo: need to setwd() to checkmate::assert_string(lockfile_path) checkmate::assert_list(opts) - checkmate::assert_character(libpaths, min.len = 1) checkmate::assert_class(sysenv, "list") + checkmate::assert_character(libpaths, min.len = 1) + checkmate::assert_directory(wd) options(opts) lapply(names(sysenv), function(sysvar) do.call(Sys.setenv, sysenv[sysvar])) .libPaths(libpaths) + setwd(wd) out <- capture.output( renv <- renv::snapshot( From 3c3d1c33fbaf84a15851e4f835ebed0738ba32da Mon Sep 17 00:00:00 2001 From: go_gonzo Date: Fri, 12 Jul 2024 14:05:34 +0200 Subject: [PATCH 33/85] setwd, todos --- R/teal_lockfile.R | 68 ++++++++++++++++++++++++----------------------- 1 file changed, 35 insertions(+), 33 deletions(-) diff --git a/R/teal_lockfile.R b/R/teal_lockfile.R index 125f9f656b..b19c7a9809 100644 --- a/R/teal_lockfile.R +++ b/R/teal_lockfile.R @@ -64,7 +64,7 @@ teal_lockfile <- function() { run = create_renv_lockfile, opts = options(), libpaths = .libPaths(), - sysenv = as.list(Sys.getenv()) # normally output is a class of "Dlist", + sysenv = as.list(Sys.getenv()), # normally output is a class of "Dlist" wd = getwd() ) logger::log_trace("Lockfile creation started based on { getwd() }.") @@ -104,55 +104,57 @@ create_renv_lockfile <- function(lockfile_path = NULL, opts, sysenv, libpaths, w #' @rdname teal_lockfile #' @keywords internal -teal_lockfile_downloadhandler <- function() { - downloadHandler( - filename = function() { - "renv.lock" - }, - content = function(file) { - teal_lockfile <- "teal_app.lock" - file.copy(teal_lockfile, file) - file - }, - contentType = "application/json" - ) +lockfile_status_tracker <- function(process) { + # todo: make sure that status doesn't need to be checked when file already existed + if (!is.null(isolate(process))) { + tracker <- observeEvent(process$status(), { + if (process$status() != "running") { + lockfile_status_handler(process) + } + }) + } } #' @rdname teal_lockfile #' @keywords internal lockfile_status_handler <- function(process) { + # todo: make sure that we catch all possible status outputs + # - normally getwd should be set to the app directory (snapshot based on the app files) + # - what happens if app directory is set to a different location? + # - what if setwd is set to a directory which doesn't contain anything + # - check out possible ERROR and WARNING renv_logs <- process$result() - message <- if (any(grepl("Lockfile written to", renv_logs$out))) { + if (any(grepl("Lockfile written to", renv_logs$out))) { with <- if (any(grepl("WARNING:", renv_logs$out))) { - "with warning(s)" + " with warning(s)" } else if (any(grepl("ERROR:", renv_logs$out))) { - "with error(s)" + " with error(s)" } else { "" } - message <- sprintf( - "Lockfile %s containing %s packages saved %s.", - renv_logs$lockfile_path, - renv_logs$length, - with - ) + logger::log_trace("Lockfile {renv_logs$lockfile_path} containing { renv_logs$length } packages created{ with }.") + shiny::showNotification("Lockfile available to download.") shinyjs::show(selector = "#teal-lockFile") } else { - "Lockfile creation failed." + warning("Lockfile creation failed.") + shiny::showNotification("Lockfile creation failed.", type = "warning") + shiny::removeUI(selector = "#teal-lockFile") } - logger::log_trace(message) - shiny::showNotification(message) } #' @rdname teal_lockfile #' @keywords internal -lockfile_status_tracker <- function(process) { - if (!is.null(isolate(process))) { - tracker <- observeEvent(process$status(), { - if (process$status() != "running") { - lockfile_status_handler(process) - } - }) - } +teal_lockfile_downloadhandler <- function() { + downloadHandler( + filename = function() { + "renv.lock" + }, + content = function(file) { + teal_lockfile <- "teal_app.lock" + file.copy(teal_lockfile, file) + file + }, + contentType = "application/json" + ) } From 42aad862fca61a0ff1f9162bf55ca14e08c3cb2e Mon Sep 17 00:00:00 2001 From: go_gonzo Date: Mon, 15 Jul 2024 03:58:54 +0200 Subject: [PATCH 34/85] tight up --- .pre-commit-config.yaml | 2 +- R/init.R | 4 +-- R/teal_lockfile.R | 65 ++++++++++++++++++++++++++--------------- man/teal_lockfile.Rd | 57 ++++++++++++++++++++++++++++++++---- 4 files changed, 97 insertions(+), 31 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7ed148fc70..3423d1678d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -17,7 +17,7 @@ repos: additional_dependencies: - davidgohel/flextable # Error: package 'flextable' is not available - davidgohel/gdtools # for flextable - - callr + - mirai - checkmate - jsonlite - lifecycle diff --git a/R/init.R b/R/init.R index ec51fde04f..533dc059b6 100644 --- a/R/init.R +++ b/R/init.R @@ -154,7 +154,7 @@ init <- function(data, teal.logger::log_system_info() # invoke lockfile creation - process <- teal_lockfile() + process <- teal_lockfile_invoke() # argument transformations ## `modules` - landing module @@ -231,7 +231,7 @@ init <- function(data, if (!is.null(landing_module)) { do.call(landing_module$server, c(list(id = "landing_module_shiny_id"), landing_module$server_args)) } - lockfile_status_tracker(process) + teal_lockfile_tracker(process) srv_teal_with_splash(id = id, data = data, modules = modules, filter = deep_copy_filter(filter)) } ) diff --git a/R/teal_lockfile.R b/R/teal_lockfile.R index b19c7a9809..d9503c7118 100644 --- a/R/teal_lockfile.R +++ b/R/teal_lockfile.R @@ -1,17 +1,27 @@ #' Generate lockfile for application's environment reproducibility #' +#' @param process (`mirai`) process to track the status of the lockfile creation. +#' @param lockfile_path (`character`) path to the lockfile (`"teal_app.lock"`). +#' @param opts (`list`) options to be set in the [mirai::daemon()]. +#' @param sysenv (`list`) system environment variables to be set in the [mirai::daemon()]. +#' @param libpaths (`character`) library paths to be set in the [mirai::daemon()]. +#' @param wd (`character(1)`) working directory to be set in the [mirai::daemon()]. +#' #' @section lockfile creation steps: #' Process is split into multiple steps. #' -#' 1. `teal_lockfile` is executed in [init] before application starts. It is better when process starts in +#' 1. `teal_lockfile_invoke` is executed in [init] before application starts. It is better when task starts in #' [init] as it is a one-time process and does not need to be repeated when each `shiny` session starts. -#' Function invokes `ExtendedTask` with `create_renv_lockfile` to begin creation of the lockfile. -#' 2. `ExtendedTask` with background `mirai` process is passed to the `lockfile_status_handler` to track the +#' Function invokes `renv_snapshot` (via `ExtendedTask`) to begin creation of the lockfile. +#' 2. `ExtendedTask` with background `mirai` process is passed to the `teal_lockfile_handler` to track the #' status of the task. -#' 3. Once `ExtendedTask` is completed, `lockfile_status_handler` is triggered to log the status of the lockfile, +#' 3. Once `ExtendedTask` is completed, `teal_lockfile_handler` is triggered to log the status of the lockfile, #' send a notification to UI and show the download button. #' 4. `teal_lockfile_downloadhandler` is used to download the lockfile (when button is shown). #' +#' Please note that if pre-computed lockfile file path has been provided through `teal.renv.lockfile` option, then +#' whole process is skipped and download lockfile button becomes available. +#' #' @section Different ways of creating lockfile: #' The function leverages [renv::snapshot()], which offers multiple methods for lockfile creation. #' @@ -34,24 +44,34 @@ #' #' @seealso [renv::snapshot()], [renv::restore()]. #' -#' @return Nothing. This function is executed for its side effect of creating a lockfile used in the `teal` application. +#' @return +#' `ExtendedTask` processing `renv` lockfile or `NULL` if lockfile has been provided through options +#' (skipping asynchronous process). +#' +#' @name teal_lockfile +#' @rdname teal_lockfile #' #' @keywords internal -teal_lockfile <- function() { +NULL + +#' @rdname teal_lockfile +#' @keywords internal +teal_lockfile_invoke <- function() { lockfile_path <- "teal_app.lock" + shiny::onStop(function() file.remove(lockfile_path)) + # If user has setup the file, there is no need to compute a new one. user_lockfile <- getOption("teal.renv.lockfile", "") if (!identical(user_lockfile, "")) { if (file.exists(user_lockfile)) { file.copy(user_lockfile, lockfile_path) logger::log_trace('Lockfile set using option "teal.renv.lockfile" - skipping automatic creation.') - return(invisible(NULL)) + return(NULL) } else { stop("lockfile provided through options('teal.renv.lockfile') does not exist.") } } - shiny::onStop(function() file.remove(lockfile_path)) process <- ExtendedTask$new(function(...) { mirai::mirai( run(lockfile_path = lockfile_path, opts = opts, sysenv = sysenv, libpaths = libpaths, wd = wd), @@ -61,7 +81,7 @@ teal_lockfile <- function() { process$invoke( lockfile_path = lockfile_path, - run = create_renv_lockfile, + run = renv_snapshot, opts = options(), libpaths = .libPaths(), sysenv = as.list(Sys.getenv()), # normally output is a class of "Dlist" @@ -74,24 +94,25 @@ teal_lockfile <- function() { #' @rdname teal_lockfile #' @keywords internal -create_renv_lockfile <- function(lockfile_path = NULL, opts, sysenv, libpaths, wd) { - # todo: need to setwd() to +renv_snapshot <- function(lockfile_path = NULL, opts, sysenv, libpaths, wd) { checkmate::assert_string(lockfile_path) checkmate::assert_list(opts) checkmate::assert_class(sysenv, "list") checkmate::assert_character(libpaths, min.len = 1) checkmate::assert_directory(wd) + + # mirai starts in vanilla session in the R.home directory. We need to pass all session related info options(opts) lapply(names(sysenv), function(sysvar) do.call(Sys.setenv, sysenv[sysvar])) .libPaths(libpaths) setwd(wd) - out <- capture.output( + out <- utils::capture.output( renv <- renv::snapshot( lockfile = lockfile_path, prompt = FALSE, force = TRUE - # type = is taken from renv::settings$snapshot.type() + # type is taken from renv::settings$snapshot.type() ) ) @@ -104,20 +125,18 @@ create_renv_lockfile <- function(lockfile_path = NULL, opts, sysenv, libpaths, w #' @rdname teal_lockfile #' @keywords internal -lockfile_status_tracker <- function(process) { - # todo: make sure that status doesn't need to be checked when file already existed - if (!is.null(isolate(process))) { - tracker <- observeEvent(process$status(), { - if (process$status() != "running") { - lockfile_status_handler(process) - } - }) - } +teal_lockfile_tracker <- function(process) { + shinyjs::hide(selector = "#teal-lockFile") + tracker <- observeEvent(process$status(), { + if (process$status() != "running") { + teal_lockfile_handler(process) + } + }) } #' @rdname teal_lockfile #' @keywords internal -lockfile_status_handler <- function(process) { +teal_lockfile_handler <- function(process) { # todo: make sure that we catch all possible status outputs # - normally getwd should be set to the app directory (snapshot based on the app files) # - what happens if app directory is set to a different location? diff --git a/man/teal_lockfile.Rd b/man/teal_lockfile.Rd index dc83582c6f..8e3bf8dc67 100644 --- a/man/teal_lockfile.Rd +++ b/man/teal_lockfile.Rd @@ -2,17 +2,63 @@ % Please edit documentation in R/teal_lockfile.R \name{teal_lockfile} \alias{teal_lockfile} -\title{Generate lockfile for application reproducibility} +\alias{teal_lockfile_invoke} +\alias{renv_snapshot} +\alias{teal_lockfile_tracker} +\alias{teal_lockfile_handler} +\alias{teal_lockfile_downloadhandler} +\title{Generate lockfile for application's environment reproducibility} \usage{ -teal_lockfile() +teal_lockfile_invoke() + +renv_snapshot(lockfile_path = NULL, opts, sysenv, libpaths, wd) + +teal_lockfile_tracker(process) + +teal_lockfile_handler(process) + +teal_lockfile_downloadhandler() +} +\arguments{ +\item{lockfile_path}{(\code{character}) path to the lockfile (\code{"teal_app.lock"}).} + +\item{opts}{(\code{list}) options to be set in the \code{\link[mirai:daemon]{mirai::daemon()}}.} + +\item{sysenv}{(\code{list}) system environment variables to be set in the \code{\link[mirai:daemon]{mirai::daemon()}}.} + +\item{libpaths}{(\code{character}) library paths to be set in the \code{\link[mirai:daemon]{mirai::daemon()}}.} + +\item{wd}{(\code{character(1)}) working directory to be set in the \code{\link[mirai:daemon]{mirai::daemon()}}.} + +\item{process}{(\code{mirai}) process to track the status of the lockfile creation.} } \value{ -Nothing. This function is executed for its side effect of creating a lockfile used in the \code{teal} application. +\code{ExtendedTask} processing \code{renv} lockfile or \code{NULL} if lockfile has been provided through options +(skipping asynchronous process). } \description{ -This function is invoked during \link{init} to create \code{renv}-compatible lockfile for use within the application. +Generate lockfile for application's environment reproducibility +} +\section{lockfile creation steps}{ + +Process is split into multiple steps. +\enumerate{ +\item \code{teal_lockfile_invoke} is executed in \link{init} before application starts. It is better when task starts in +\link{init} as it is a one-time process and does not need to be repeated when each \code{shiny} session starts. +Function invokes \code{renv_snapshot} (via \code{ExtendedTask}) to begin creation of the lockfile. +\item \code{ExtendedTask} with background \code{mirai} process is passed to the \code{teal_lockfile_handler} to track the +status of the task. +\item Once \code{ExtendedTask} is completed, \code{teal_lockfile_handler} is triggered to log the status of the lockfile, +send a notification to UI and show the download button. +\item \code{teal_lockfile_downloadhandler} is used to download the lockfile (when button is shown). +} + +Please note that if precomputed lockfile file path has been provided through \code{teal.renv.lockfile} option, then +whole process is skipped and download lockfile button becomes available. } -\details{ + +\section{Different ways of creating lockfile}{ + The function leverages \code{\link[renv:snapshot]{renv::snapshot()}}, which offers multiple methods for lockfile creation. \itemize{ \item User-specified: @@ -34,6 +80,7 @@ directory, set \code{renv::settings$snapshot.type("explicit")}. The naming conve } } } + \section{lockfile usage}{ After creating the lockfile, you can restore the application environment using \code{renv::restore()}. From 8431ef9946dc17d057dbe04dc7c29bfb2b833e87 Mon Sep 17 00:00:00 2001 From: go_gonzo Date: Mon, 15 Jul 2024 09:20:18 +0200 Subject: [PATCH 35/85] fixes --- R/teal_lockfile.R | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/R/teal_lockfile.R b/R/teal_lockfile.R index d9503c7118..3001718c34 100644 --- a/R/teal_lockfile.R +++ b/R/teal_lockfile.R @@ -126,12 +126,17 @@ renv_snapshot <- function(lockfile_path = NULL, opts, sysenv, libpaths, wd) { #' @rdname teal_lockfile #' @keywords internal teal_lockfile_tracker <- function(process) { - shinyjs::hide(selector = "#teal-lockFile") - tracker <- observeEvent(process$status(), { - if (process$status() != "running") { - teal_lockfile_handler(process) - } - }) + checkmate::assert_class(process, "ExtendedTask", null.ok = TRUE) + if (inherits(process, "ExtendedTask")) { + tracker <- observeEvent(process$status(), { + if (process$status() != "running") { + teal_lockfile_handler(process) + } + }) + } else { + # if it is not mirai then lockfile was provided through options (skip the process - file is ready) + shinyjs::show("teal-lockFile") + } } #' @rdname teal_lockfile @@ -153,12 +158,11 @@ teal_lockfile_handler <- function(process) { } logger::log_trace("Lockfile {renv_logs$lockfile_path} containing { renv_logs$length } packages created{ with }.") - shiny::showNotification("Lockfile available to download.") - shinyjs::show(selector = "#teal-lockFile") + shiny::showNotification("Lockfile is available to download.") + shinyjs::show("teal-lockFile") } else { warning("Lockfile creation failed.") shiny::showNotification("Lockfile creation failed.", type = "warning") - shiny::removeUI(selector = "#teal-lockFile") } } From af2c6a324167cb747d50674685bc1673fb649cf2 Mon Sep 17 00:00:00 2001 From: "27856297+dependabot-preview[bot]@users.noreply.github.com" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 15 Jul 2024 07:25:45 +0000 Subject: [PATCH 36/85] [skip roxygen] [skip vbump] Roxygen Man Pages Auto Update --- man/teal_lockfile.Rd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/man/teal_lockfile.Rd b/man/teal_lockfile.Rd index 8e3bf8dc67..00d2d9444d 100644 --- a/man/teal_lockfile.Rd +++ b/man/teal_lockfile.Rd @@ -53,7 +53,7 @@ send a notification to UI and show the download button. \item \code{teal_lockfile_downloadhandler} is used to download the lockfile (when button is shown). } -Please note that if precomputed lockfile file path has been provided through \code{teal.renv.lockfile} option, then +Please note that if pre-computed lockfile file path has been provided through \code{teal.renv.lockfile} option, then whole process is skipped and download lockfile button becomes available. } From aa8a66905253538df44365a51da86355aff49e21 Mon Sep 17 00:00:00 2001 From: Marcin <133694481+m7pr@users.noreply.github.com> Date: Tue, 16 Jul 2024 11:27:50 +0200 Subject: [PATCH 37/85] Update R/teal_lockfile.R Co-authored-by: Aleksander Chlebowski <114988527+chlebowa@users.noreply.github.com> Signed-off-by: Marcin <133694481+m7pr@users.noreply.github.com> --- R/teal_lockfile.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/teal_lockfile.R b/R/teal_lockfile.R index 3001718c34..318cf68d3e 100644 --- a/R/teal_lockfile.R +++ b/R/teal_lockfile.R @@ -103,7 +103,7 @@ renv_snapshot <- function(lockfile_path = NULL, opts, sysenv, libpaths, wd) { # mirai starts in vanilla session in the R.home directory. We need to pass all session related info options(opts) - lapply(names(sysenv), function(sysvar) do.call(Sys.setenv, sysenv[sysvar])) + do.call(Sys.setenv, sysenv) .libPaths(libpaths) setwd(wd) From 1f094285aaf65a10254396a28d44e0ad6aa8dd1b Mon Sep 17 00:00:00 2001 From: m7pr Date: Tue, 16 Jul 2024 11:50:22 +0200 Subject: [PATCH 38/85] udpate comment --- R/teal_lockfile.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/teal_lockfile.R b/R/teal_lockfile.R index 318cf68d3e..1bf8af8558 100644 --- a/R/teal_lockfile.R +++ b/R/teal_lockfile.R @@ -134,7 +134,7 @@ teal_lockfile_tracker <- function(process) { } }) } else { - # if it is not mirai then lockfile was provided through options (skip the process - file is ready) + # If it is not mirai proces, then lockfile was provided through options. Skip the process - file is ready. shinyjs::show("teal-lockFile") } } From d77f4860494b918e7ccc796873a0ad6f4d662d44 Mon Sep 17 00:00:00 2001 From: m7pr Date: Tue, 16 Jul 2024 13:17:57 +0200 Subject: [PATCH 39/85] run_mirai as function --- R/teal_lockfile.R | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/R/teal_lockfile.R b/R/teal_lockfile.R index 1bf8af8558..0e21673972 100644 --- a/R/teal_lockfile.R +++ b/R/teal_lockfile.R @@ -72,16 +72,23 @@ teal_lockfile_invoke <- function() { } } - process <- ExtendedTask$new(function(...) { + + run_mirai <- function(lockfile_path, opts, sysenv, libpaths, wd) { mirai::mirai( - run(lockfile_path = lockfile_path, opts = opts, sysenv = sysenv, libpaths = libpaths, wd = wd), - ... + renv_snapshot(lockfile_path = lockfile_path, opts = opts, sysenv = sysenv, libpaths = libpaths, wd = wd), ) - }) + } + process <- ExtendedTask$new(run_mirai) + # + # process <- ExtendedTask$new(function(...) { + # mirai::mirai( + # run(lockfile_path = lockfile_path, opts = opts, sysenv = sysenv, libpaths = libpaths, wd = wd), + # ... + # ) + # }) process$invoke( lockfile_path = lockfile_path, - run = renv_snapshot, opts = options(), libpaths = .libPaths(), sysenv = as.list(Sys.getenv()), # normally output is a class of "Dlist" @@ -158,9 +165,10 @@ teal_lockfile_handler <- function(process) { } logger::log_trace("Lockfile {renv_logs$lockfile_path} containing { renv_logs$length } packages created{ with }.") - shiny::showNotification("Lockfile is available to download.") + shiny::showNotification(paste0("Lockfile created", with, " and available to download.")) shinyjs::show("teal-lockFile") } else { + # Unsure it will ever happen. warning("Lockfile creation failed.") shiny::showNotification("Lockfile creation failed.", type = "warning") } From 10a517d50b88d7a996caffff8d8ec40cc3044f4e Mon Sep 17 00:00:00 2001 From: m7pr Date: Tue, 16 Jul 2024 14:12:16 +0200 Subject: [PATCH 40/85] cleanup names for new run_renv_mirai function --- R/teal_lockfile.R | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/R/teal_lockfile.R b/R/teal_lockfile.R index 0e21673972..2da127b8e4 100644 --- a/R/teal_lockfile.R +++ b/R/teal_lockfile.R @@ -73,19 +73,19 @@ teal_lockfile_invoke <- function() { } - run_mirai <- function(lockfile_path, opts, sysenv, libpaths, wd) { + run_renv_mirai <- function(lockfile_path, opts, sysenv, libpaths, wd) { mirai::mirai( - renv_snapshot(lockfile_path = lockfile_path, opts = opts, sysenv = sysenv, libpaths = libpaths, wd = wd), + renv_snapshot( + lockfile_path = lockfile_path, + opts = opts, + sysenv = sysenv, + libpaths = libpaths, + wd = wd + ), ) } - process <- ExtendedTask$new(run_mirai) - # - # process <- ExtendedTask$new(function(...) { - # mirai::mirai( - # run(lockfile_path = lockfile_path, opts = opts, sysenv = sysenv, libpaths = libpaths, wd = wd), - # ... - # ) - # }) + + process <- ExtendedTask$new(run_renv_mirai) process$invoke( lockfile_path = lockfile_path, @@ -168,7 +168,6 @@ teal_lockfile_handler <- function(process) { shiny::showNotification(paste0("Lockfile created", with, " and available to download.")) shinyjs::show("teal-lockFile") } else { - # Unsure it will ever happen. warning("Lockfile creation failed.") shiny::showNotification("Lockfile creation failed.", type = "warning") } From 667b751257d119a1b40e4b2a0624c09d230716f4 Mon Sep 17 00:00:00 2001 From: m7pr Date: Wed, 17 Jul 2024 10:45:18 +0200 Subject: [PATCH 41/85] remove comments --- R/teal_lockfile.R | 5 ----- 1 file changed, 5 deletions(-) diff --git a/R/teal_lockfile.R b/R/teal_lockfile.R index 2da127b8e4..dcd0a9f052 100644 --- a/R/teal_lockfile.R +++ b/R/teal_lockfile.R @@ -149,11 +149,6 @@ teal_lockfile_tracker <- function(process) { #' @rdname teal_lockfile #' @keywords internal teal_lockfile_handler <- function(process) { - # todo: make sure that we catch all possible status outputs - # - normally getwd should be set to the app directory (snapshot based on the app files) - # - what happens if app directory is set to a different location? - # - what if setwd is set to a directory which doesn't contain anything - # - check out possible ERROR and WARNING renv_logs <- process$result() if (any(grepl("Lockfile written to", renv_logs$out))) { with <- if (any(grepl("WARNING:", renv_logs$out))) { From 7849e40b7af8459ca1d41724fd5d1d14fe90cf48 Mon Sep 17 00:00:00 2001 From: m7pr Date: Wed, 17 Jul 2024 10:46:48 +0200 Subject: [PATCH 42/85] remove whiteline --- R/teal_lockfile.R | 1 - 1 file changed, 1 deletion(-) diff --git a/R/teal_lockfile.R b/R/teal_lockfile.R index dcd0a9f052..245bcf9572 100644 --- a/R/teal_lockfile.R +++ b/R/teal_lockfile.R @@ -72,7 +72,6 @@ teal_lockfile_invoke <- function() { } } - run_renv_mirai <- function(lockfile_path, opts, sysenv, libpaths, wd) { mirai::mirai( renv_snapshot( From 323d8ae836e7717a67413715c0e998ae31600bed Mon Sep 17 00:00:00 2001 From: Marcin <133694481+m7pr@users.noreply.github.com> Date: Wed, 17 Jul 2024 12:26:24 +0200 Subject: [PATCH 43/85] Update R/teal_lockfile.R MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Dawid Kałędkowski <6959016+gogonzo@users.noreply.github.com> Signed-off-by: Marcin <133694481+m7pr@users.noreply.github.com> --- R/teal_lockfile.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/teal_lockfile.R b/R/teal_lockfile.R index 245bcf9572..e02d651e79 100644 --- a/R/teal_lockfile.R +++ b/R/teal_lockfile.R @@ -80,7 +80,7 @@ teal_lockfile_invoke <- function() { sysenv = sysenv, libpaths = libpaths, wd = wd - ), + ) ) } From 50871635c9bacf73d5d32980e31bbba5897d3fc7 Mon Sep 17 00:00:00 2001 From: m7pr Date: Wed, 17 Jul 2024 12:28:58 +0200 Subject: [PATCH 44/85] let's try with teal:::renv_lockfile --- R/teal_lockfile.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/teal_lockfile.R b/R/teal_lockfile.R index e02d651e79..d0d3c41327 100644 --- a/R/teal_lockfile.R +++ b/R/teal_lockfile.R @@ -74,7 +74,7 @@ teal_lockfile_invoke <- function() { run_renv_mirai <- function(lockfile_path, opts, sysenv, libpaths, wd) { mirai::mirai( - renv_snapshot( + teal:::renv_snapshot( lockfile_path = lockfile_path, opts = opts, sysenv = sysenv, From 38e4e17e69c33dabd09060cba0734ea3ed98ac85 Mon Sep 17 00:00:00 2001 From: m7pr Date: Wed, 17 Jul 2024 13:46:40 +0200 Subject: [PATCH 45/85] get back to the previous version of exeuciton of ExtendedTask, but pass parameters by names --- R/teal_lockfile.R | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/R/teal_lockfile.R b/R/teal_lockfile.R index d0d3c41327..d9d07dded7 100644 --- a/R/teal_lockfile.R +++ b/R/teal_lockfile.R @@ -72,21 +72,27 @@ teal_lockfile_invoke <- function() { } } - run_renv_mirai <- function(lockfile_path, opts, sysenv, libpaths, wd) { - mirai::mirai( - teal:::renv_snapshot( + process <- ExtendedTask$new( + function(run, lockfile_path = lockfile_path, opts = opts, sysenv = sysenv, libpaths = libpaths, wd = wd) { + mirai::mirai( + run( + lockfile_path = lockfile_path, + opts = opts, + sysenv = sysenv, + libpaths = libpaths, + wd = wd + ), lockfile_path = lockfile_path, opts = opts, sysenv = sysenv, libpaths = libpaths, wd = wd ) - ) - } - - process <- ExtendedTask$new(run_renv_mirai) + } + ) process$invoke( + run = renv_snapshot, lockfile_path = lockfile_path, opts = options(), libpaths = .libPaths(), From 043d6733aefff11c27a32a9985a3e1f5dcdbdf77 Mon Sep 17 00:00:00 2001 From: m7pr Date: Wed, 17 Jul 2024 13:58:53 +0200 Subject: [PATCH 46/85] suppress socket warning --- R/teal_lockfile.R | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/R/teal_lockfile.R b/R/teal_lockfile.R index d9d07dded7..e93ef8fa7f 100644 --- a/R/teal_lockfile.R +++ b/R/teal_lockfile.R @@ -82,6 +82,7 @@ teal_lockfile_invoke <- function() { libpaths = libpaths, wd = wd ), + run = run, lockfile_path = lockfile_path, opts = opts, sysenv = sysenv, @@ -91,14 +92,16 @@ teal_lockfile_invoke <- function() { } ) - process$invoke( - run = renv_snapshot, - lockfile_path = lockfile_path, - opts = options(), - libpaths = .libPaths(), - sysenv = as.list(Sys.getenv()), # normally output is a class of "Dlist" - wd = getwd() - ) + suppressWarnings({ # 'package:stats' may not be available when loading + process$invoke( + run = teal:::renv_snapshot, + lockfile_path = lockfile_path, + opts = options(), + libpaths = .libPaths(), + sysenv = as.list(Sys.getenv()), # normally output is a class of "Dlist" + wd = getwd() + ) + }) logger::log_trace("Lockfile creation started based on { getwd() }.") process From 4b26fb843af7ff871b5e7b14e8be02db0e15de9f Mon Sep 17 00:00:00 2001 From: m7pr Date: Wed, 17 Jul 2024 14:02:02 +0200 Subject: [PATCH 47/85] styler --- R/teal_lockfile.R | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/R/teal_lockfile.R b/R/teal_lockfile.R index e93ef8fa7f..503fe3816d 100644 --- a/R/teal_lockfile.R +++ b/R/teal_lockfile.R @@ -75,19 +75,8 @@ teal_lockfile_invoke <- function() { process <- ExtendedTask$new( function(run, lockfile_path = lockfile_path, opts = opts, sysenv = sysenv, libpaths = libpaths, wd = wd) { mirai::mirai( - run( - lockfile_path = lockfile_path, - opts = opts, - sysenv = sysenv, - libpaths = libpaths, - wd = wd - ), - run = run, - lockfile_path = lockfile_path, - opts = opts, - sysenv = sysenv, - libpaths = libpaths, - wd = wd + run(lockfile_path = lockfile_path, opts = opts, sysenv = sysenv, libpaths = libpaths, wd = wd), + run = run, lockfile_path = lockfile_path, opts = opts, sysenv = sysenv, libpaths = libpaths, wd = wd ) } ) From 717bad267dfc71e31749200a1c245e0b1680bbc2 Mon Sep 17 00:00:00 2001 From: m7pr Date: Wed, 17 Jul 2024 14:40:28 +0200 Subject: [PATCH 48/85] remove prefix --- R/teal_lockfile.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/teal_lockfile.R b/R/teal_lockfile.R index 503fe3816d..f475cf8a7b 100644 --- a/R/teal_lockfile.R +++ b/R/teal_lockfile.R @@ -83,7 +83,7 @@ teal_lockfile_invoke <- function() { suppressWarnings({ # 'package:stats' may not be available when loading process$invoke( - run = teal:::renv_snapshot, + run = renv_snapshot, lockfile_path = lockfile_path, opts = options(), libpaths = .libPaths(), From c6ccbb57ace5d705b76333149f0a70e6fab0f6af Mon Sep 17 00:00:00 2001 From: Pawel Rucki <12943682+pawelru@users.noreply.github.com> Date: Mon, 29 Jul 2024 10:54:14 +0200 Subject: [PATCH 49/85] 479 mirai pr (#1272) most of my code review comments please note that the target is a feature branch --------- Signed-off-by: Pawel Rucki <12943682+pawelru@users.noreply.github.com> Co-authored-by: Marcin <133694481+m7pr@users.noreply.github.com> --- DESCRIPTION | 6 +- R/init.R | 8 ++- R/module_teal.R | 5 +- R/teal_lockfile.R | 149 ++++++++++++++++++++++++------------------- man/teal_lockfile.Rd | 20 +++--- 5 files changed, 105 insertions(+), 83 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index e22e6ec4d7..2177dbaf39 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -40,13 +40,13 @@ Depends: teal.data (>= 0.5.0), teal.slice (>= 0.5.0) Imports: - mirai, checkmate (>= 2.1.0), jsonlite, lifecycle (>= 0.2.0), logger (>= 0.2.0), magrittr (>= 1.5), methods, + mirai, renv (>= 1.0.7), rlang (>= 1.0.0), shinyjs, @@ -73,9 +73,9 @@ VignetteBuilder: RdMacros: lifecycle Config/Needs/verdepcheck: rstudio/shiny, insightsengineering/teal.data, - insightsengineering/teal.slice, shikokuchuo/mirai, mllg/checkmate, + insightsengineering/teal.slice, mllg/checkmate, jeroen/jsonlite, r-lib/lifecycle, - daroczig/logger, tidyverse/magrittr, rstudio/renv, + daroczig/logger, tidyverse/magrittr, shikokuchuo/mirai, shikokuchuo/nanonext, rstudio/renv, r-lib/rlang, daattali/shinyjs, insightsengineering/teal.logger, insightsengineering/teal.reporter, insightsengineering/teal.widgets, rstudio/bslib, yihui/knitr, bioc::MultiAssayExperiment, r-lib/R6, diff --git a/R/init.R b/R/init.R index 533dc059b6..e3152df2ba 100644 --- a/R/init.R +++ b/R/init.R @@ -154,7 +154,7 @@ init <- function(data, teal.logger::log_system_info() # invoke lockfile creation - process <- teal_lockfile_invoke() + lockfile_process <- teal_lockfile() # argument transformations ## `modules` - landing module @@ -231,7 +231,11 @@ init <- function(data, if (!is.null(landing_module)) { do.call(landing_module$server, c(list(id = "landing_module_shiny_id"), landing_module$server_args)) } - teal_lockfile_tracker(process) + + if (!is.null(lockfile_process)) { + teal_lockfile_process_tracker(lockfile_process) + } + srv_teal_with_splash(id = id, data = data, modules = modules, filter = deep_copy_filter(filter)) } ) diff --git a/R/module_teal.R b/R/module_teal.R index 43bfcd4e91..0d38b558ad 100644 --- a/R/module_teal.R +++ b/R/module_teal.R @@ -112,7 +112,8 @@ ui_teal <- function(id, footer, teal.widgets::verbatim_popup_ui(ns("sessionInfo"), "Session Info", type = "link"), br(), - shinyjs::hidden(downloadLink(ns("lockFile"), "Download .lock file")), + tags$span("", id = ns("lockFileStatus")), + shinyjs::disabled(downloadLink(ns("lockFileLink"), "Download lockfile")), textOutput(ns("identifier")) ) ) @@ -136,7 +137,7 @@ srv_teal <- function(id, modules, teal_data_rv, filter = teal_slices()) { title = "SessionInfo" ) - output$lockFile <- teal_lockfile_downloadhandler() + output$lockFileLink <- teal_lockfile_downloadhandler() # `JavaScript` code run_js_files(files = "init.js") diff --git a/R/teal_lockfile.R b/R/teal_lockfile.R index f475cf8a7b..a9035ee6db 100644 --- a/R/teal_lockfile.R +++ b/R/teal_lockfile.R @@ -56,41 +56,68 @@ NULL #' @rdname teal_lockfile #' @keywords internal -teal_lockfile_invoke <- function() { +teal_lockfile <- function() { lockfile_path <- "teal_app.lock" - shiny::onStop(function() file.remove(lockfile_path)) + shiny::onStop(function() { + if (file.exists(lockfile_path)) { + file.remove(lockfile_path) + } + }) - # If user has setup the file, there is no need to compute a new one. user_lockfile <- getOption("teal.renv.lockfile", "") if (!identical(user_lockfile, "")) { - if (file.exists(user_lockfile)) { - file.copy(user_lockfile, lockfile_path) - logger::log_trace('Lockfile set using option "teal.renv.lockfile" - skipping automatic creation.') - return(NULL) - } else { - stop("lockfile provided through options('teal.renv.lockfile') does not exist.") - } + teal_lockfile_external() + } else { + teal_lockfile_process_invoke() + } +} + +#' @rdname teal_lockfile +#' @keywords internal +teal_lockfile_external <- function() { + lockfile_path <- "teal_app.lock" + user_lockfile <- getOption("teal.renv.lockfile", "") + + if (file.exists(user_lockfile)) { + file.copy(user_lockfile, lockfile_path) + shinyjs::enable("teal-lockFileLink") + logger::log_trace('Lockfile set using option "teal.renv.lockfile" - skipping automatic creation.') + return(NULL) + } else { + stop("lockfile provided through options('teal.renv.lockfile') does not exist.") } +} + +#' @rdname teal_lockfile +#' @keywords internal +teal_lockfile_process_invoke <- function() { + lockfile_path <- "teal_app.lock" process <- ExtendedTask$new( - function(run, lockfile_path = lockfile_path, opts = opts, sysenv = sysenv, libpaths = libpaths, wd = wd) { + function() { mirai::mirai( - run(lockfile_path = lockfile_path, opts = opts, sysenv = sysenv, libpaths = libpaths, wd = wd), - run = run, lockfile_path = lockfile_path, opts = opts, sysenv = sysenv, libpaths = libpaths, wd = wd + { + options(opts) + do.call(Sys.setenv, sysenv) + .libPaths(libpaths) + setwd(wd) + + run(lockfile_path = lockfile_path) + }, + run = renv_snapshot, + lockfile_path = lockfile_path, + opts = options(), + libpaths = .libPaths(), + sysenv = as.list(Sys.getenv()), + wd = getwd() ) } ) suppressWarnings({ # 'package:stats' may not be available when loading - process$invoke( - run = renv_snapshot, - lockfile_path = lockfile_path, - opts = options(), - libpaths = .libPaths(), - sysenv = as.list(Sys.getenv()), # normally output is a class of "Dlist" - wd = getwd() - ) + process$invoke() }) + logger::log_trace("Lockfile creation started based on { getwd() }.") process @@ -98,21 +125,11 @@ teal_lockfile_invoke <- function() { #' @rdname teal_lockfile #' @keywords internal -renv_snapshot <- function(lockfile_path = NULL, opts, sysenv, libpaths, wd) { +renv_snapshot <- function(lockfile_path) { checkmate::assert_string(lockfile_path) - checkmate::assert_list(opts) - checkmate::assert_class(sysenv, "list") - checkmate::assert_character(libpaths, min.len = 1) - checkmate::assert_directory(wd) - - # mirai starts in vanilla session in the R.home directory. We need to pass all session related info - options(opts) - do.call(Sys.setenv, sysenv) - .libPaths(libpaths) - setwd(wd) out <- utils::capture.output( - renv <- renv::snapshot( + res <- renv::snapshot( lockfile = lockfile_path, prompt = FALSE, force = TRUE @@ -122,47 +139,45 @@ renv_snapshot <- function(lockfile_path = NULL, opts, sysenv, libpaths, wd) { list( out = out, - lockfile_path = lockfile_path, - length = length(renv$Packages) + res = res, + path = lockfile_path ) } #' @rdname teal_lockfile #' @keywords internal -teal_lockfile_tracker <- function(process) { - checkmate::assert_class(process, "ExtendedTask", null.ok = TRUE) - if (inherits(process, "ExtendedTask")) { - tracker <- observeEvent(process$status(), { - if (process$status() != "running") { - teal_lockfile_handler(process) - } - }) - } else { - # If it is not mirai proces, then lockfile was provided through options. Skip the process - file is ready. - shinyjs::show("teal-lockFile") - } -} +teal_lockfile_process_tracker <- function(process) { + checkmate::assert_class(process, "ExtendedTask") -#' @rdname teal_lockfile -#' @keywords internal -teal_lockfile_handler <- function(process) { - renv_logs <- process$result() - if (any(grepl("Lockfile written to", renv_logs$out))) { - with <- if (any(grepl("WARNING:", renv_logs$out))) { - " with warning(s)" - } else if (any(grepl("ERROR:", renv_logs$out))) { - " with error(s)" - } else { - "" - } + observeEvent(process$status(), { + if (process$status() == "initial" || process$status() == "running") { + shinyjs::html("teal-lockFileStatus", "Creating lockfile...") + } else if (process$status() == "success") { + result <- process$result() + if (any(grepl("Lockfile written to", result$out))) { + logger::log_trace("Lockfile {result$path} containing { length(result$res$Packages) } packages created.") + if (any(grepl("WARNING:", result$out)) || any(grepl("ERROR:", result$out))) { + logger::log_warn("Lockfile created with warning(s) or error(s):") + for (i in result$out) { + logger::log_warn(i) + } + } - logger::log_trace("Lockfile {renv_logs$lockfile_path} containing { renv_logs$length } packages created{ with }.") - shiny::showNotification(paste0("Lockfile created", with, " and available to download.")) - shinyjs::show("teal-lockFile") - } else { - warning("Lockfile creation failed.") - shiny::showNotification("Lockfile creation failed.", type = "warning") - } + shinyjs::html("teal-lockFileStatus", "Application lockfile ready.") + shinyjs::hide("teal-lockFileStatus", anim = TRUE) + + shinyjs::enable("teal-lockFileLink") + } else { + warning("Lockfile creation failed.") + shinyjs::html("teal-lockFileStatus", "Lockfile creation failed.") + shinyjs::disable("teal-lockFileLink") + } + } else if (process$status() == "error") { + warning("Lockfile creation failed.") + shinyjs::html("teal-lockFileStatus", "Lockfile creation failed.") + shinyjs::disable("teal-lockFileLink") + } + }) } #' @rdname teal_lockfile diff --git a/man/teal_lockfile.Rd b/man/teal_lockfile.Rd index 00d2d9444d..5d935c6d4e 100644 --- a/man/teal_lockfile.Rd +++ b/man/teal_lockfile.Rd @@ -2,26 +2,30 @@ % Please edit documentation in R/teal_lockfile.R \name{teal_lockfile} \alias{teal_lockfile} -\alias{teal_lockfile_invoke} +\alias{teal_lockfile_external} +\alias{teal_lockfile_process_invoke} \alias{renv_snapshot} -\alias{teal_lockfile_tracker} -\alias{teal_lockfile_handler} +\alias{teal_lockfile_process_tracker} \alias{teal_lockfile_downloadhandler} \title{Generate lockfile for application's environment reproducibility} \usage{ -teal_lockfile_invoke() +teal_lockfile() -renv_snapshot(lockfile_path = NULL, opts, sysenv, libpaths, wd) +teal_lockfile_external() -teal_lockfile_tracker(process) +teal_lockfile_process_invoke() -teal_lockfile_handler(process) +renv_snapshot(lockfile_path) + +teal_lockfile_process_tracker(process) teal_lockfile_downloadhandler() } \arguments{ \item{lockfile_path}{(\code{character}) path to the lockfile (\code{"teal_app.lock"}).} +\item{process}{(\code{mirai}) process to track the status of the lockfile creation.} + \item{opts}{(\code{list}) options to be set in the \code{\link[mirai:daemon]{mirai::daemon()}}.} \item{sysenv}{(\code{list}) system environment variables to be set in the \code{\link[mirai:daemon]{mirai::daemon()}}.} @@ -29,8 +33,6 @@ teal_lockfile_downloadhandler() \item{libpaths}{(\code{character}) library paths to be set in the \code{\link[mirai:daemon]{mirai::daemon()}}.} \item{wd}{(\code{character(1)}) working directory to be set in the \code{\link[mirai:daemon]{mirai::daemon()}}.} - -\item{process}{(\code{mirai}) process to track the status of the lockfile creation.} } \value{ \code{ExtendedTask} processing \code{renv} lockfile or \code{NULL} if lockfile has been provided through options From f746bd6be00f14ebc6c703fb622f21eaab5c3ed2 Mon Sep 17 00:00:00 2001 From: go_gonzo Date: Wed, 14 Aug 2024 08:09:16 +0200 Subject: [PATCH 50/85] duplicated test - there is one in init already --- tests/testthat/test-utils.R | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/tests/testthat/test-utils.R b/tests/testthat/test-utils.R index 3c74679f1e..4b8f45bc8b 100644 --- a/tests/testthat/test-utils.R +++ b/tests/testthat/test-utils.R @@ -195,14 +195,3 @@ testthat::test_that("defunction recursively goes down a list", { y ) }) - -testthat::test_that("create_renv_lockfile creates a lock file during the execution", { - old_plan <- future::plan(future::sequential) - withr::defer(future::plan(old_plan)) - - renv_file_name <- "teal_app.lock" - withr::defer(file.remove(renv_file_name)) - promise <- create_renv_lockfile(TRUE, renv_file_name) - - testthat::expect_true(file.exists(renv_file_name)) -}) \ No newline at end of file From 5a8089c8b8b9dc203d9d1c795938d66083d8e3c5 Mon Sep 17 00:00:00 2001 From: go_gonzo Date: Wed, 14 Aug 2024 08:09:56 +0200 Subject: [PATCH 51/85] fix condition --- R/teal_lockfile.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/R/teal_lockfile.R b/R/teal_lockfile.R index a9035ee6db..e005283595 100644 --- a/R/teal_lockfile.R +++ b/R/teal_lockfile.R @@ -150,13 +150,13 @@ teal_lockfile_process_tracker <- function(process) { checkmate::assert_class(process, "ExtendedTask") observeEvent(process$status(), { - if (process$status() == "initial" || process$status() == "running") { + if (process$status() %in% c("initial", "running")) { shinyjs::html("teal-lockFileStatus", "Creating lockfile...") } else if (process$status() == "success") { result <- process$result() if (any(grepl("Lockfile written to", result$out))) { logger::log_trace("Lockfile {result$path} containing { length(result$res$Packages) } packages created.") - if (any(grepl("WARNING:", result$out)) || any(grepl("ERROR:", result$out))) { + if (any(grepl("(WARNING|ERROR):", result$out))) { logger::log_warn("Lockfile created with warning(s) or error(s):") for (i in result$out) { logger::log_warn(i) From ee55fa859e93c0e0827bdf3f4b6520afd623546f Mon Sep 17 00:00:00 2001 From: go_gonzo Date: Wed, 14 Aug 2024 08:10:08 +0200 Subject: [PATCH 52/85] fix r cmd check --- R/teal_lockfile.R | 7 +------ man/teal_lockfile.Rd | 8 -------- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/R/teal_lockfile.R b/R/teal_lockfile.R index e005283595..725be7381c 100644 --- a/R/teal_lockfile.R +++ b/R/teal_lockfile.R @@ -2,10 +2,6 @@ #' #' @param process (`mirai`) process to track the status of the lockfile creation. #' @param lockfile_path (`character`) path to the lockfile (`"teal_app.lock"`). -#' @param opts (`list`) options to be set in the [mirai::daemon()]. -#' @param sysenv (`list`) system environment variables to be set in the [mirai::daemon()]. -#' @param libpaths (`character`) library paths to be set in the [mirai::daemon()]. -#' @param wd (`character(1)`) working directory to be set in the [mirai::daemon()]. #' #' @section lockfile creation steps: #' Process is split into multiple steps. @@ -88,11 +84,11 @@ teal_lockfile_external <- function() { } } +utils::globalVariables(c("opts", "sysenv", "libpaths", "wd", "lockfilepath", "run")) # needed for mirai call #' @rdname teal_lockfile #' @keywords internal teal_lockfile_process_invoke <- function() { lockfile_path <- "teal_app.lock" - process <- ExtendedTask$new( function() { mirai::mirai( @@ -101,7 +97,6 @@ teal_lockfile_process_invoke <- function() { do.call(Sys.setenv, sysenv) .libPaths(libpaths) setwd(wd) - run(lockfile_path = lockfile_path) }, run = renv_snapshot, diff --git a/man/teal_lockfile.Rd b/man/teal_lockfile.Rd index 5d935c6d4e..0e544552db 100644 --- a/man/teal_lockfile.Rd +++ b/man/teal_lockfile.Rd @@ -25,14 +25,6 @@ teal_lockfile_downloadhandler() \item{lockfile_path}{(\code{character}) path to the lockfile (\code{"teal_app.lock"}).} \item{process}{(\code{mirai}) process to track the status of the lockfile creation.} - -\item{opts}{(\code{list}) options to be set in the \code{\link[mirai:daemon]{mirai::daemon()}}.} - -\item{sysenv}{(\code{list}) system environment variables to be set in the \code{\link[mirai:daemon]{mirai::daemon()}}.} - -\item{libpaths}{(\code{character}) library paths to be set in the \code{\link[mirai:daemon]{mirai::daemon()}}.} - -\item{wd}{(\code{character(1)}) working directory to be set in the \code{\link[mirai:daemon]{mirai::daemon()}}.} } \value{ \code{ExtendedTask} processing \code{renv} lockfile or \code{NULL} if lockfile has been provided through options From 198ae35920aa5cea88b6ba2063eddaa0cc708d27 Mon Sep 17 00:00:00 2001 From: go_gonzo Date: Wed, 14 Aug 2024 08:36:01 +0200 Subject: [PATCH 53/85] encapsulate everything in one module --- DESCRIPTION | 2 +- R/init.R | 7 -- R/module_teal.R | 7 +- R/teal_lockfile.R | 176 +++++++++++++++++++------------------------ man/teal_lockfile.Rd | 18 ++--- 5 files changed, 90 insertions(+), 120 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index c49aef68c0..94017d3eff 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -45,7 +45,7 @@ Imports: lifecycle (>= 0.2.0), logger (>= 0.2.0), methods, - mirai, + mirai (>= 1.1.1), renv (>= 1.0.7), rlang (>= 1.0.0), shinyjs, diff --git a/R/init.R b/R/init.R index 5653a0325f..cf69e7983a 100644 --- a/R/init.R +++ b/R/init.R @@ -157,9 +157,6 @@ init <- function(data, # log teal.logger::log_system_info() - # invoke lockfile creation - lockfile_process <- teal_lockfile() - # argument transformations ## `modules` - landing module landing <- extract_module(modules, "teal_module_landing") @@ -257,10 +254,6 @@ init <- function(data, if (!is.null(landing_popup)) { do.call(landing_popup$server, c(list(id = "landing_module_shiny_id"), landing_popup$server_args)) } - - if (!is.null(lockfile_process)) { - teal_lockfile_process_tracker(lockfile_process) - } srv_teal(id = ns("teal"), data = data, modules = modules, filter = deep_copy_filter(filter)) } ) diff --git a/R/module_teal.R b/R/module_teal.R index 5b61a3ec08..8bc4f0a070 100644 --- a/R/module_teal.R +++ b/R/module_teal.R @@ -139,8 +139,7 @@ ui_teal <- function(id, footer, teal.widgets::verbatim_popup_ui(ns("sessionInfo"), "Session Info", type = "link"), br(), - tags$span("", id = ns("lockFileStatus")), - shinyjs::disabled(downloadLink(ns("lockFileLink"), "Download lockfile")), + ui_teal_lockfile(ns("lockfile")), textOutput(ns("identifier")) ) ) @@ -158,6 +157,8 @@ srv_teal <- function(id, data, modules, filter = teal_slices()) { moduleServer(id, function(input, output, session) { logger::log_debug("srv_teal initializing.") + srv_teal_lockfile("lockfile") + output$identifier <- renderText( paste0("Pid:", Sys.getpid(), " Token:", substr(session$token, 25, 32)) ) @@ -168,8 +169,6 @@ srv_teal <- function(id, data, modules, filter = teal_slices()) { title = "SessionInfo" ) - output$lockFileLink <- teal_lockfile_downloadhandler() - # `JavaScript` code run_js_files(files = "init.js") diff --git a/R/teal_lockfile.R b/R/teal_lockfile.R index 725be7381c..d2cdabdbe9 100644 --- a/R/teal_lockfile.R +++ b/R/teal_lockfile.R @@ -1,25 +1,9 @@ #' Generate lockfile for application's environment reproducibility #' -#' @param process (`mirai`) process to track the status of the lockfile creation. #' @param lockfile_path (`character`) path to the lockfile (`"teal_app.lock"`). #' -#' @section lockfile creation steps: -#' Process is split into multiple steps. -#' -#' 1. `teal_lockfile_invoke` is executed in [init] before application starts. It is better when task starts in -#' [init] as it is a one-time process and does not need to be repeated when each `shiny` session starts. -#' Function invokes `renv_snapshot` (via `ExtendedTask`) to begin creation of the lockfile. -#' 2. `ExtendedTask` with background `mirai` process is passed to the `teal_lockfile_handler` to track the -#' status of the task. -#' 3. Once `ExtendedTask` is completed, `teal_lockfile_handler` is triggered to log the status of the lockfile, -#' send a notification to UI and show the download button. -#' 4. `teal_lockfile_downloadhandler` is used to download the lockfile (when button is shown). -#' -#' Please note that if pre-computed lockfile file path has been provided through `teal.renv.lockfile` option, then -#' whole process is skipped and download lockfile button becomes available. -#' #' @section Different ways of creating lockfile: -#' The function leverages [renv::snapshot()], which offers multiple methods for lockfile creation. +#' `teal` leverages [renv::snapshot()], which offers multiple methods for lockfile creation. #' #' - User-specified: #' - **Pre-computed lockfile**: Users can provide their own pre-computed lockfile by specifying the path via @@ -40,20 +24,75 @@ #' #' @seealso [renv::snapshot()], [renv::restore()]. #' -#' @return -#' `ExtendedTask` processing `renv` lockfile or `NULL` if lockfile has been provided through options -#' (skipping asynchronous process). +#' @return `NULL` #' -#' @name teal_lockfile -#' @rdname teal_lockfile +#' @name module_teal_lockfile +#' @rdname module_teal_lockfile #' #' @keywords internal NULL -#' @rdname teal_lockfile -#' @keywords internal -teal_lockfile <- function() { - lockfile_path <- "teal_app.lock" +#' @rdname module_teal_lockfile +ui_teal_lockfile <- function(id) { + ns <- NS(id) + shiny::tagList( + tags$span("", id = ns("lockFileStatus")), + shinyjs::disabled(downloadLink(ns("lockFileLink"), "Download lockfile")) + ) +} + +#' @rdname module_teal_lockfile +srv_teal_lockfile <- function(id) { + moduleServer(id, function(input, output, session) { + logger::log_debug("Initialize srv_teal_lockfile.") + lockfile_path <- "teal_app.lock" + process <- .teal_lockfile(lockfile_path) + + observeEvent(process$status(), { + if (process$status() == "initial" || process$status() == "running") { + shinyjs::html("lockFileStatus", "Creating lockfile...") + } else if (process$status() == "success") { + result <- process$result() + if (any(grepl("Lockfile written to", result$out))) { + logger::log_debug("Lockfile {result$path} containing { length(result$res$Packages) } packages created.") + if (any(grepl("WARNING:", result$out)) || any(grepl("ERROR:", result$out))) { + warning("Lockfile created with warning(s) or error(s):") + for (i in result$out) { + warning(i) + } + } + shinyjs::html("lockFileStatus", "Application lockfile ready.") + shinyjs::hide("lockFileStatus", anim = TRUE) + shinyjs::enable("lockFileLink") + } else { + warning("Lockfile creation failed.") + shinyjs::html("lockFileStatus", "Lockfile creation failed.") + shinyjs::disable("lockFileLink") + } + } else if (process$status() == "error") { + warning("Lockfile creation failed.") + shinyjs::html("lockFileStatus", "Lockfile creation failed.") + shinyjs::disable("lockFileLink") + } + }) + + output$lockFileLink <- downloadHandler( + filename = function() { + "renv.lock" + }, + content = function(file) { + file.copy(lockfile_path, file) + file + }, + contentType = "application/json" + ) + + NULL + }) +} + +#' @rdname module_teal_lockfile +.teal_lockfile <- function(lockfile_path) { shiny::onStop(function() { if (file.exists(lockfile_path)) { file.remove(lockfile_path) @@ -62,22 +101,20 @@ teal_lockfile <- function() { user_lockfile <- getOption("teal.renv.lockfile", "") if (!identical(user_lockfile, "")) { - teal_lockfile_external() + .teal_lockfile_external(lockfile_path) } else { - teal_lockfile_process_invoke() + .teal_lockfile_process_invoke(lockfile_path) } } -#' @rdname teal_lockfile -#' @keywords internal -teal_lockfile_external <- function() { - lockfile_path <- "teal_app.lock" +#' @rdname module_teal_lockfile +.teal_lockfile_external <- function(lockfile_path) { user_lockfile <- getOption("teal.renv.lockfile", "") if (file.exists(user_lockfile)) { file.copy(user_lockfile, lockfile_path) - shinyjs::enable("teal-lockFileLink") - logger::log_trace('Lockfile set using option "teal.renv.lockfile" - skipping automatic creation.') + shinyjs::enable("lockFileLink") + logger::log_debug('Lockfile set using option "teal.renv.lockfile" - skipping automatic creation.') return(NULL) } else { stop("lockfile provided through options('teal.renv.lockfile') does not exist.") @@ -85,10 +122,8 @@ teal_lockfile_external <- function() { } utils::globalVariables(c("opts", "sysenv", "libpaths", "wd", "lockfilepath", "run")) # needed for mirai call -#' @rdname teal_lockfile -#' @keywords internal -teal_lockfile_process_invoke <- function() { - lockfile_path <- "teal_app.lock" +#' @rdname module_teal_lockfile +.teal_lockfile_process_invoke <- function(lockfile_path) { process <- ExtendedTask$new( function() { mirai::mirai( @@ -99,7 +134,7 @@ teal_lockfile_process_invoke <- function() { setwd(wd) run(lockfile_path = lockfile_path) }, - run = renv_snapshot, + run = .renv_snapshot, lockfile_path = lockfile_path, opts = options(), libpaths = .libPaths(), @@ -113,22 +148,19 @@ teal_lockfile_process_invoke <- function() { process$invoke() }) - logger::log_trace("Lockfile creation started based on { getwd() }.") + logger::log_debug("Lockfile creation started based on { getwd() }.") process } -#' @rdname teal_lockfile -#' @keywords internal -renv_snapshot <- function(lockfile_path) { - checkmate::assert_string(lockfile_path) - +#' @rdname module_teal_lockfile +.renv_snapshot <- function(lockfile_path) { out <- utils::capture.output( res <- renv::snapshot( lockfile = lockfile_path, prompt = FALSE, force = TRUE - # type is taken from renv::settings$snapshot.type() + # default type argument is taken from renv::settings$snapshot.type() ) ) @@ -138,55 +170,3 @@ renv_snapshot <- function(lockfile_path) { path = lockfile_path ) } - -#' @rdname teal_lockfile -#' @keywords internal -teal_lockfile_process_tracker <- function(process) { - checkmate::assert_class(process, "ExtendedTask") - - observeEvent(process$status(), { - if (process$status() %in% c("initial", "running")) { - shinyjs::html("teal-lockFileStatus", "Creating lockfile...") - } else if (process$status() == "success") { - result <- process$result() - if (any(grepl("Lockfile written to", result$out))) { - logger::log_trace("Lockfile {result$path} containing { length(result$res$Packages) } packages created.") - if (any(grepl("(WARNING|ERROR):", result$out))) { - logger::log_warn("Lockfile created with warning(s) or error(s):") - for (i in result$out) { - logger::log_warn(i) - } - } - - shinyjs::html("teal-lockFileStatus", "Application lockfile ready.") - shinyjs::hide("teal-lockFileStatus", anim = TRUE) - - shinyjs::enable("teal-lockFileLink") - } else { - warning("Lockfile creation failed.") - shinyjs::html("teal-lockFileStatus", "Lockfile creation failed.") - shinyjs::disable("teal-lockFileLink") - } - } else if (process$status() == "error") { - warning("Lockfile creation failed.") - shinyjs::html("teal-lockFileStatus", "Lockfile creation failed.") - shinyjs::disable("teal-lockFileLink") - } - }) -} - -#' @rdname teal_lockfile -#' @keywords internal -teal_lockfile_downloadhandler <- function() { - downloadHandler( - filename = function() { - "renv.lock" - }, - content = function(file) { - teal_lockfile <- "teal_app.lock" - file.copy(teal_lockfile, file) - file - }, - contentType = "application/json" - ) -} diff --git a/man/teal_lockfile.Rd b/man/teal_lockfile.Rd index 0e544552db..1064c08bc0 100644 --- a/man/teal_lockfile.Rd +++ b/man/teal_lockfile.Rd @@ -2,29 +2,27 @@ % Please edit documentation in R/teal_lockfile.R \name{teal_lockfile} \alias{teal_lockfile} +\alias{ui_teal_lockfile} +\alias{srv_teal_lockfile} \alias{teal_lockfile_external} \alias{teal_lockfile_process_invoke} \alias{renv_snapshot} -\alias{teal_lockfile_process_tracker} -\alias{teal_lockfile_downloadhandler} \title{Generate lockfile for application's environment reproducibility} \usage{ -teal_lockfile() +ui_teal_lockfile(id) -teal_lockfile_external() +srv_teal_lockfile(id) -teal_lockfile_process_invoke() +teal_lockfile(lockfile_path) -renv_snapshot(lockfile_path) +teal_lockfile_external(lockfile_path) -teal_lockfile_process_tracker(process) +teal_lockfile_process_invoke(lockfile_path) -teal_lockfile_downloadhandler() +renv_snapshot(lockfile_path) } \arguments{ \item{lockfile_path}{(\code{character}) path to the lockfile (\code{"teal_app.lock"}).} - -\item{process}{(\code{mirai}) process to track the status of the lockfile creation.} } \value{ \code{ExtendedTask} processing \code{renv} lockfile or \code{NULL} if lockfile has been provided through options From 39cfa532bd169df30c74a8e52caf03ff4accf807 Mon Sep 17 00:00:00 2001 From: "27856297+dependabot-preview[bot]@users.noreply.github.com" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 14 Aug 2024 06:40:53 +0000 Subject: [PATCH 54/85] [skip roxygen] [skip vbump] Roxygen Man Pages Auto Update --- ...al_lockfile.Rd => module_teal_lockfile.Rd} | 42 ++++++------------- 1 file changed, 12 insertions(+), 30 deletions(-) rename man/{teal_lockfile.Rd => module_teal_lockfile.Rd} (55%) diff --git a/man/teal_lockfile.Rd b/man/module_teal_lockfile.Rd similarity index 55% rename from man/teal_lockfile.Rd rename to man/module_teal_lockfile.Rd index 1064c08bc0..951ef7f8ed 100644 --- a/man/teal_lockfile.Rd +++ b/man/module_teal_lockfile.Rd @@ -1,57 +1,39 @@ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/teal_lockfile.R -\name{teal_lockfile} -\alias{teal_lockfile} +\name{module_teal_lockfile} +\alias{module_teal_lockfile} \alias{ui_teal_lockfile} \alias{srv_teal_lockfile} -\alias{teal_lockfile_external} -\alias{teal_lockfile_process_invoke} -\alias{renv_snapshot} +\alias{.teal_lockfile} +\alias{.teal_lockfile_external} +\alias{.teal_lockfile_process_invoke} +\alias{.renv_snapshot} \title{Generate lockfile for application's environment reproducibility} \usage{ ui_teal_lockfile(id) srv_teal_lockfile(id) -teal_lockfile(lockfile_path) +.teal_lockfile(lockfile_path) -teal_lockfile_external(lockfile_path) +.teal_lockfile_external(lockfile_path) -teal_lockfile_process_invoke(lockfile_path) +.teal_lockfile_process_invoke(lockfile_path) -renv_snapshot(lockfile_path) +.renv_snapshot(lockfile_path) } \arguments{ \item{lockfile_path}{(\code{character}) path to the lockfile (\code{"teal_app.lock"}).} } \value{ -\code{ExtendedTask} processing \code{renv} lockfile or \code{NULL} if lockfile has been provided through options -(skipping asynchronous process). +\code{NULL} } \description{ Generate lockfile for application's environment reproducibility } -\section{lockfile creation steps}{ - -Process is split into multiple steps. -\enumerate{ -\item \code{teal_lockfile_invoke} is executed in \link{init} before application starts. It is better when task starts in -\link{init} as it is a one-time process and does not need to be repeated when each \code{shiny} session starts. -Function invokes \code{renv_snapshot} (via \code{ExtendedTask}) to begin creation of the lockfile. -\item \code{ExtendedTask} with background \code{mirai} process is passed to the \code{teal_lockfile_handler} to track the -status of the task. -\item Once \code{ExtendedTask} is completed, \code{teal_lockfile_handler} is triggered to log the status of the lockfile, -send a notification to UI and show the download button. -\item \code{teal_lockfile_downloadhandler} is used to download the lockfile (when button is shown). -} - -Please note that if pre-computed lockfile file path has been provided through \code{teal.renv.lockfile} option, then -whole process is skipped and download lockfile button becomes available. -} - \section{Different ways of creating lockfile}{ -The function leverages \code{\link[renv:snapshot]{renv::snapshot()}}, which offers multiple methods for lockfile creation. +\code{teal} leverages \code{\link[renv:snapshot]{renv::snapshot()}}, which offers multiple methods for lockfile creation. \itemize{ \item User-specified: \itemize{ From 5312d61b28afd078843fb23979b1325a6eeb4cad Mon Sep 17 00:00:00 2001 From: go_gonzo Date: Wed, 14 Aug 2024 12:33:46 +0200 Subject: [PATCH 55/85] - no shinyjs in utility functions - warning in favour of log_warn --- R/teal_lockfile.R | 94 +++++++++++++++++++------------------ man/module_teal_lockfile.Rd | 6 --- 2 files changed, 48 insertions(+), 52 deletions(-) diff --git a/R/teal_lockfile.R b/R/teal_lockfile.R index d2cdabdbe9..87b6cd6a30 100644 --- a/R/teal_lockfile.R +++ b/R/teal_lockfile.R @@ -45,34 +45,65 @@ ui_teal_lockfile <- function(id) { srv_teal_lockfile <- function(id) { moduleServer(id, function(input, output, session) { logger::log_debug("Initialize srv_teal_lockfile.") + enable_lockfile_download <- function() { + shinyjs::html("lockFileStatus", "Application lockfile ready.") + shinyjs::hide("lockFileStatus", anim = TRUE) + shinyjs::enable("lockFileLink") + } + disable_lockfile_download <- function() { + warning("Lockfile creation failed.", call. = FALSE) + shinyjs::html("lockFileStatus", "Lockfile creation failed.") + shinyjs::disable("lockFileLink") + } + shiny::onStop(function() { + if (file.exists(lockfile_path) && !shiny::isRunning()) { + logger::log_debug("Removing lockfile after shutting down the app") + file.remove(lockfile_path) + } + }) + lockfile_path <- "teal_app.lock" - process <- .teal_lockfile(lockfile_path) + user_lockfile <- getOption("teal.renv.lockfile", "") + # run renv::snapshot only once per app + if (file.exists(lockfile_path)) { + logger::log_debug("Lockfile is already created - skipping automatic creation.") + enable_lockfile_download() + return(NULL) + } + # don't run renv::snapshot when option is set + if (!identical(user_lockfile, "")) { + if (file.exists(user_lockfile)) { + file.copy(getOption("teal.renv.lockfile", ""), lockfile_path) + logger::log_debug('Lockfile set using option "teal.renv.lockfile" - skipping automatic creation.') + enable_lockfile_download() + return(NULL) + } else { + warning("lockfile provided through options('teal.renv.lockfile') does not exist.", call. = FALSE) + } + } + + # should be run only if the lockfile doesn't exist + process <- .teal_lockfile_process_invoke(lockfile_path) observeEvent(process$status(), { - if (process$status() == "initial" || process$status() == "running") { + if (process$status() %in% c("initial", "running")) { shinyjs::html("lockFileStatus", "Creating lockfile...") } else if (process$status() == "success") { result <- process$result() if (any(grepl("Lockfile written to", result$out))) { logger::log_debug("Lockfile {result$path} containing { length(result$res$Packages) } packages created.") - if (any(grepl("WARNING:", result$out)) || any(grepl("ERROR:", result$out))) { - warning("Lockfile created with warning(s) or error(s):") + if (any(grepl("(WARNING|ERROR):", result$out))) { + warning("Lockfile created with warning(s) or error(s):", call. = FALSE) for (i in result$out) { - warning(i) + warning(i, call. = FALSE) } } - shinyjs::html("lockFileStatus", "Application lockfile ready.") - shinyjs::hide("lockFileStatus", anim = TRUE) - shinyjs::enable("lockFileLink") + enable_lockfile_download() } else { - warning("Lockfile creation failed.") - shinyjs::html("lockFileStatus", "Lockfile creation failed.") - shinyjs::disable("lockFileLink") + disable_lockfile_download() } } else if (process$status() == "error") { - warning("Lockfile creation failed.") - shinyjs::html("lockFileStatus", "Lockfile creation failed.") - shinyjs::disable("lockFileLink") + disable_lockfile_download() } }) @@ -87,38 +118,9 @@ srv_teal_lockfile <- function(id) { contentType = "application/json" ) - NULL - }) -} -#' @rdname module_teal_lockfile -.teal_lockfile <- function(lockfile_path) { - shiny::onStop(function() { - if (file.exists(lockfile_path)) { - file.remove(lockfile_path) - } + NULL }) - - user_lockfile <- getOption("teal.renv.lockfile", "") - if (!identical(user_lockfile, "")) { - .teal_lockfile_external(lockfile_path) - } else { - .teal_lockfile_process_invoke(lockfile_path) - } -} - -#' @rdname module_teal_lockfile -.teal_lockfile_external <- function(lockfile_path) { - user_lockfile <- getOption("teal.renv.lockfile", "") - - if (file.exists(user_lockfile)) { - file.copy(user_lockfile, lockfile_path) - shinyjs::enable("lockFileLink") - logger::log_debug('Lockfile set using option "teal.renv.lockfile" - skipping automatic creation.') - return(NULL) - } else { - stop("lockfile provided through options('teal.renv.lockfile') does not exist.") - } } utils::globalVariables(c("opts", "sysenv", "libpaths", "wd", "lockfilepath", "run")) # needed for mirai call @@ -159,8 +161,8 @@ utils::globalVariables(c("opts", "sysenv", "libpaths", "wd", "lockfilepath", "ru res <- renv::snapshot( lockfile = lockfile_path, prompt = FALSE, - force = TRUE - # default type argument is taken from renv::settings$snapshot.type() + force = TRUE, + type = renv::settings$snapshot.type() # see the section "Different ways of creating lockfile" above here ) ) diff --git a/man/module_teal_lockfile.Rd b/man/module_teal_lockfile.Rd index 951ef7f8ed..c5173eb0c5 100644 --- a/man/module_teal_lockfile.Rd +++ b/man/module_teal_lockfile.Rd @@ -4,8 +4,6 @@ \alias{module_teal_lockfile} \alias{ui_teal_lockfile} \alias{srv_teal_lockfile} -\alias{.teal_lockfile} -\alias{.teal_lockfile_external} \alias{.teal_lockfile_process_invoke} \alias{.renv_snapshot} \title{Generate lockfile for application's environment reproducibility} @@ -14,10 +12,6 @@ ui_teal_lockfile(id) srv_teal_lockfile(id) -.teal_lockfile(lockfile_path) - -.teal_lockfile_external(lockfile_path) - .teal_lockfile_process_invoke(lockfile_path) .renv_snapshot(lockfile_path) From 6d0409f5061520965071e6db125089bee47259f5 Mon Sep 17 00:00:00 2001 From: go_gonzo Date: Wed, 14 Aug 2024 16:51:17 +0200 Subject: [PATCH 56/85] - don't make a "teal_app.renv" when the app is interrupted during async process --- R/teal_lockfile.R | 57 +++++++++++++++++-------------- tests/testthat/test-init.R | 17 --------- tests/testthat/test-module_teal.R | 24 +++++++++++++ 3 files changed, 56 insertions(+), 42 deletions(-) diff --git a/R/teal_lockfile.R b/R/teal_lockfile.R index 87b6cd6a30..d542b5a726 100644 --- a/R/teal_lockfile.R +++ b/R/teal_lockfile.R @@ -62,19 +62,19 @@ srv_teal_lockfile <- function(id) { } }) + # run renv::snapshot only once per app. Once calculated available for all users lockfile_path <- "teal_app.lock" - user_lockfile <- getOption("teal.renv.lockfile", "") - # run renv::snapshot only once per app if (file.exists(lockfile_path)) { - logger::log_debug("Lockfile is already created - skipping automatic creation.") + logger::log_debug("Lockfile have been already created - skipping automatic creation.") enable_lockfile_download() return(NULL) } # don't run renv::snapshot when option is set + user_lockfile <- getOption("teal.renv.lockfile", "") if (!identical(user_lockfile, "")) { if (file.exists(user_lockfile)) { - file.copy(getOption("teal.renv.lockfile", ""), lockfile_path) + file.copy(user_lockfile, lockfile_path) logger::log_debug('Lockfile set using option "teal.renv.lockfile" - skipping automatic creation.') enable_lockfile_download() return(NULL) @@ -83,8 +83,11 @@ srv_teal_lockfile <- function(id) { } } - # should be run only if the lockfile doesn't exist - process <- .teal_lockfile_process_invoke(lockfile_path) + # - Will be run only if the lockfile doesn't exist (see the if-s above) + # - We render to the tempfile because the process might last after session is closed and we don't + # want to make a "teal_app.renv" then. This is why we copy only during active session. + temp_lockfile_path <- tempfile() + process <- .teal_lockfile_process_invoke(temp_lockfile_path) observeEvent(process$status(), { if (process$status() %in% c("initial", "running")) { shinyjs::html("lockFileStatus", "Creating lockfile...") @@ -98,6 +101,7 @@ srv_teal_lockfile <- function(id) { warning(i, call. = FALSE) } } + file.copy(temp_lockfile_path, lockfile_path) enable_lockfile_download() } else { disable_lockfile_download() @@ -118,7 +122,6 @@ srv_teal_lockfile <- function(id) { contentType = "application/json" ) - NULL }) } @@ -126,25 +129,29 @@ srv_teal_lockfile <- function(id) { utils::globalVariables(c("opts", "sysenv", "libpaths", "wd", "lockfilepath", "run")) # needed for mirai call #' @rdname module_teal_lockfile .teal_lockfile_process_invoke <- function(lockfile_path) { - process <- ExtendedTask$new( - function() { - mirai::mirai( - { - options(opts) - do.call(Sys.setenv, sysenv) - .libPaths(libpaths) - setwd(wd) - run(lockfile_path = lockfile_path) - }, - run = .renv_snapshot, - lockfile_path = lockfile_path, - opts = options(), - libpaths = .libPaths(), - sysenv = as.list(Sys.getenv()), - wd = getwd() - ) - } + # to ignore running mirai process after app is closed + mirai_obj <- mirai::mirai( + { + options(opts) + do.call(Sys.setenv, sysenv) + .libPaths(libpaths) + setwd(wd) + run(lockfile_path = lockfile_path) + }, + run = .renv_snapshot, + lockfile_path = lockfile_path, + opts = options(), + libpaths = .libPaths(), + sysenv = as.list(Sys.getenv()), + wd = getwd() ) + shiny::onStop(function() { + if (mirai::unresolved(mirai_obj)) { + logger::log_debug("Terminating a running lockfile process...") + mirai::stop_mirai(mirai_obj) # this doesn't stop running - renv will be created even if session is closed + } + }) + process <- ExtendedTask$new(function() mirai_obj) suppressWarnings({ # 'package:stats' may not be available when loading process$invoke() diff --git a/tests/testthat/test-init.R b/tests/testthat/test-init.R index 80ed15c388..24efa4d119 100644 --- a/tests/testthat/test-init.R +++ b/tests/testthat/test-init.R @@ -78,20 +78,3 @@ testthat::test_that("init throws when dataname in filter incompatible w/ datanam "Filter 'iris Species' refers to dataname not available in 'data'" ) }) - -testthat::test_that("init creates a lockfile during the execution", { - renv_file_name <- "teal_app.lock" - withr::defer(file.remove(renv_file_name)) - app <- init( - data = teal.data::teal_data(iris = iris), - modules = example_module(label = "example teal module") - ) - - iter <- 1 - while (!file.exists(renv_file_name) && iter <= 100) { - Sys.sleep(0.25) - iter <- iter + 1 # max wait time is 25 seconds - } - - testthat::expect_true(file.exists(renv_file_name)) -}) diff --git a/tests/testthat/test-module_teal.R b/tests/testthat/test-module_teal.R index b308406a54..3fd87afb06 100644 --- a/tests/testthat/test-module_teal.R +++ b/tests/testthat/test-module_teal.R @@ -79,6 +79,30 @@ transform_list <<- list( ) ) +testthat::describe("srv_teal lockfile", { + testthat::it("process is invoked and snapshot is created after relevant time the session ends", { + renv_file_name <- "teal_app.lock" + shiny::testServer( + app = srv_teal, + args = list( + id = "test", + data = teal.data::teal_data(iris = iris), + modules = modules(example_module()) + ), + expr = { + iter <- 1 + while (!file.exists(renv_file_name) && iter <= 100) { + Sys.sleep(0.25) + iter <- iter + 1 # max wait time is 25 seconds + } + testthat::expect_true(file.exists(renv_file_name)) + } + ) + testthat::expect_false(file.exists(renv_file_name)) + }) +}) + + testthat::describe("srv_teal arguments", { testthat::it("accepts data to be teal_data", { testthat::expect_no_error( From e59c5c350309289936ff3b77ab3a42fc4a607259 Mon Sep 17 00:00:00 2001 From: go_gonzo Date: Wed, 14 Aug 2024 16:56:09 +0200 Subject: [PATCH 57/85] remove future and promises from verdepcheck --- DESCRIPTION | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 97f1e0a322..8470ac204b 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -73,8 +73,8 @@ RdMacros: lifecycle Config/Needs/verdepcheck: rstudio/shiny, insightsengineering/teal.data, insightsengineering/teal.slice, mllg/checkmate, - HenrikBengtsson/future, jeroen/jsonlite, r-lib/lifecycle, - daroczig/logger, rstudio/promises, rstudio/renv, r-lib/rlang, + jeroen/jsonlite, r-lib/lifecycle, + daroczig/logger, rstudio/renv, r-lib/rlang, daattali/shinyjs, insightsengineering/teal.code, insightsengineering/teal.logger, insightsengineering/teal.reporter, insightsengineering/teal.widgets, rstudio/bslib, yihui/knitr, From bcc115ed85bb377e3c8857fa1eff6c99cf22ff21 Mon Sep 17 00:00:00 2001 From: go_gonzo Date: Mon, 19 Aug 2024 13:25:46 +0200 Subject: [PATCH 58/85] WIP option to enable/disable snapshot --- DESCRIPTION | 2 +- R/{teal_lockfile.R => module_teal_lockfile.R} | 24 ++++--- R/zzz.R | 8 ++- man/module_teal_lockfile.Rd | 2 +- tests/testthat/setup-options.R | 27 +++----- tests/testthat/test-module_teal.R | 63 ++++++++++++++----- 6 files changed, 75 insertions(+), 51 deletions(-) rename R/{teal_lockfile.R => module_teal_lockfile.R} (93%) diff --git a/DESCRIPTION b/DESCRIPTION index 50525a75f9..90a5ce6500 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -105,6 +105,7 @@ Collate: 'module_snapshot_manager.R' 'module_teal.R' 'module_teal_data.R' + 'module_teal_lockfile.R' 'module_teal_with_splash.R' 'module_transform_data.R' 'reporter_previewer_module.R' @@ -115,7 +116,6 @@ Collate: 'teal_data_module-eval_code.R' 'teal_data_module-within.R' 'teal_data_utils.R' - 'teal_lockfile.R' 'teal_reporter.R' 'teal_slices-store.R' 'teal_slices.R' diff --git a/R/teal_lockfile.R b/R/module_teal_lockfile.R similarity index 93% rename from R/teal_lockfile.R rename to R/module_teal_lockfile.R index d542b5a726..3913249664 100644 --- a/R/teal_lockfile.R +++ b/R/module_teal_lockfile.R @@ -35,6 +35,9 @@ NULL #' @rdname module_teal_lockfile ui_teal_lockfile <- function(id) { ns <- NS(id) + if (!isTRUE(getOption("teal.renv.enable"))) { + return(NULL) + } shiny::tagList( tags$span("", id = ns("lockFileStatus")), shinyjs::disabled(downloadLink(ns("lockFileLink"), "Download lockfile")) @@ -55,6 +58,12 @@ srv_teal_lockfile <- function(id) { shinyjs::html("lockFileStatus", "Lockfile creation failed.") shinyjs::disable("lockFileLink") } + + if (!isTRUE(getOption("teal.renv.enable"))) { + return(NULL) + } + + lockfile_path <- "teal_app.lock" shiny::onStop(function() { if (file.exists(lockfile_path) && !shiny::isRunning()) { logger::log_debug("Removing lockfile after shutting down the app") @@ -63,7 +72,6 @@ srv_teal_lockfile <- function(id) { }) # run renv::snapshot only once per app. Once calculated available for all users - lockfile_path <- "teal_app.lock" if (file.exists(lockfile_path)) { logger::log_debug("Lockfile have been already created - skipping automatic creation.") enable_lockfile_download() @@ -87,21 +95,21 @@ srv_teal_lockfile <- function(id) { # - We render to the tempfile because the process might last after session is closed and we don't # want to make a "teal_app.renv" then. This is why we copy only during active session. temp_lockfile_path <- tempfile() - process <- .teal_lockfile_process_invoke(temp_lockfile_path) + process <- .teal_lockfile_process_invoke(lockfile_path) observeEvent(process$status(), { if (process$status() %in% c("initial", "running")) { shinyjs::html("lockFileStatus", "Creating lockfile...") } else if (process$status() == "success") { result <- process$result() if (any(grepl("Lockfile written to", result$out))) { - logger::log_debug("Lockfile {result$path} containing { length(result$res$Packages) } packages created.") + logger::log_debug("Lockfile containing { length(result$res$Packages) } packages created.") if (any(grepl("(WARNING|ERROR):", result$out))) { warning("Lockfile created with warning(s) or error(s):", call. = FALSE) for (i in result$out) { warning(i, call. = FALSE) } } - file.copy(temp_lockfile_path, lockfile_path) + # file.copy(temp_lockfile_path, lockfile_path) enable_lockfile_download() } else { disable_lockfile_download() @@ -151,7 +159,7 @@ utils::globalVariables(c("opts", "sysenv", "libpaths", "wd", "lockfilepath", "ru mirai::stop_mirai(mirai_obj) # this doesn't stop running - renv will be created even if session is closed } }) - process <- ExtendedTask$new(function() mirai_obj) + process <- shiny::ExtendedTask$new(function() mirai_obj) suppressWarnings({ # 'package:stats' may not be available when loading process$invoke() @@ -173,9 +181,5 @@ utils::globalVariables(c("opts", "sysenv", "libpaths", "wd", "lockfilepath", "ru ) ) - list( - out = out, - res = res, - path = lockfile_path - ) + list(out = out, res = res) } diff --git a/R/zzz.R b/R/zzz.R index 817f9bae4b..87ca241f27 100644 --- a/R/zzz.R +++ b/R/zzz.R @@ -1,13 +1,15 @@ .onLoad <- function(libname, pkgname) { # adapted from https://github.com/r-lib/devtools/blob/master/R/zzz.R - teal_default_options <- list(teal.show_js_log = FALSE) + teal_default_options <- list( + teal.show_js_log = FALSE, + teal.renv.enable = TRUE, + shiny.sanitize.errors = FALSE + ) op <- options() toset <- !(names(teal_default_options) %in% names(op)) if (any(toset)) options(teal_default_options[toset]) - options("shiny.sanitize.errors" = FALSE) - # Set up the teal logger instance teal.logger::register_logger("teal") teal.logger::register_handlers("teal") diff --git a/man/module_teal_lockfile.Rd b/man/module_teal_lockfile.Rd index c5173eb0c5..45ab22abb3 100644 --- a/man/module_teal_lockfile.Rd +++ b/man/module_teal_lockfile.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/teal_lockfile.R +% Please edit documentation in R/module_teal_lockfile.R \name{module_teal_lockfile} \alias{module_teal_lockfile} \alias{ui_teal_lockfile} diff --git a/tests/testthat/setup-options.R b/tests/testthat/setup-options.R index 78be1f9b5f..1ee67b5a6f 100644 --- a/tests/testthat/setup-options.R +++ b/tests/testthat/setup-options.R @@ -1,20 +1,9 @@ -# `opts_partial_match_old` is left for exclusions due to partial matching in dependent packages (i.e. not fixable here) -# it might happen that it is not used right now, but it is left for possible future use -# use with: `withr::with_options(opts_partial_match_old, { ... })` inside the test -opts_partial_match_old <- list( - warnPartialMatchDollar = getOption("warnPartialMatchDollar"), - warnPartialMatchArgs = getOption("warnPartialMatchArgs"), - warnPartialMatchAttr = getOption("warnPartialMatchAttr") +withr::local_options( + list( + warnPartialMatchDollar = TRUE, + warnPartialMatchArgs = TRUE, + warnPartialMatchAttr = TRUE, + teal.renv.enable = FALSE + ), + .local_envir = testthat::teardown_env() ) -opts_partial_match_new <- list( - warnPartialMatchDollar = TRUE, - warnPartialMatchArgs = TRUE, - warnPartialMatchAttr = TRUE -) - -if (isFALSE(getFromNamespace("on_cran", "testthat")()) && requireNamespace("withr", quietly = TRUE)) { - withr::local_options( - opts_partial_match_new, - .local_envir = testthat::teardown_env() - ) -} diff --git a/tests/testthat/test-module_teal.R b/tests/testthat/test-module_teal.R index 3fd87afb06..3927cb2d19 100644 --- a/tests/testthat/test-module_teal.R +++ b/tests/testthat/test-module_teal.R @@ -80,29 +80,58 @@ transform_list <<- list( ) testthat::describe("srv_teal lockfile", { - testthat::it("process is invoked and snapshot is created after relevant time the session ends", { - renv_file_name <- "teal_app.lock" - shiny::testServer( - app = srv_teal, - args = list( - id = "test", - data = teal.data::teal_data(iris = iris), - modules = modules(example_module()) + testthat::it("creation process is invoked and snapshot is copied to teal_app.lock", { + withr::with_options( + list(teal.renv.enable = TRUE), + { + renv_filename <- "teal_app.lock" + shiny::testServer( + app = srv_teal, + args = list( + id = "test", + data = teal.data::teal_data(iris = iris), + modules = modules(example_module()) + ), + expr = { + iter <- 1 + while (!file.exists(renv_filename) && iter <= 100) { + Sys.sleep(0.25) + iter <- iter + 1 # max wait time is 25 seconds + } + testthat::expect_true(file.exists(renv_filename)) + } + ) + testthat::expect_false(file.exists(renv_filename)) + } + ) + }) + testthat::it("is copied from option-teal.renv.lockfile and then removed from an app directory", { + temp_lockfile_mock <- tempfile() + writeLines("test", temp_lockfile_mock) + withr::with_options( + list( + teal.renv.enable = TRUE, + teal.renv.lockfile = temp_lockfile_mock ), - expr = { - iter <- 1 - while (!file.exists(renv_file_name) && iter <= 100) { - Sys.sleep(0.25) - iter <- iter + 1 # max wait time is 25 seconds - } - testthat::expect_true(file.exists(renv_file_name)) + { + renv_filename <- "teal_app.lock" + shiny::testServer( + app = srv_teal, + args = list( + id = "test", + data = teal.data::teal_data(iris = iris), + modules = modules(example_module()) + ), + expr = { + testthat::expect_true(file.exists(renv_filename)) + } + ) } ) - testthat::expect_false(file.exists(renv_file_name)) + testthat::expect_false(file.exists(renv_filename)) }) }) - testthat::describe("srv_teal arguments", { testthat::it("accepts data to be teal_data", { testthat::expect_no_error( From 5fab6ce9c1821f4b1b225e0108f888718ae87d40 Mon Sep 17 00:00:00 2001 From: go_gonzo Date: Mon, 19 Aug 2024 21:18:56 +0200 Subject: [PATCH 59/85] switch to Joe's Cheng example as one can't just create mirai object and use it in extended task --- R/module_teal_lockfile.R | 42 ++++++++++++++++--------------- tests/testthat/test-module_teal.R | 2 +- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/R/module_teal_lockfile.R b/R/module_teal_lockfile.R index 3913249664..253086fee5 100644 --- a/R/module_teal_lockfile.R +++ b/R/module_teal_lockfile.R @@ -94,7 +94,6 @@ srv_teal_lockfile <- function(id) { # - Will be run only if the lockfile doesn't exist (see the if-s above) # - We render to the tempfile because the process might last after session is closed and we don't # want to make a "teal_app.renv" then. This is why we copy only during active session. - temp_lockfile_path <- tempfile() process <- .teal_lockfile_process_invoke(lockfile_path) observeEvent(process$status(), { if (process$status() %in% c("initial", "running")) { @@ -109,7 +108,6 @@ srv_teal_lockfile <- function(id) { warning(i, call. = FALSE) } } - # file.copy(temp_lockfile_path, lockfile_path) enable_lockfile_download() } else { disable_lockfile_download() @@ -119,7 +117,7 @@ srv_teal_lockfile <- function(id) { } }) - output$lockFileLink <- downloadHandler( + output$lockFileLink <- shiny::downloadHandler( filename = function() { "renv.lock" }, @@ -137,29 +135,33 @@ srv_teal_lockfile <- function(id) { utils::globalVariables(c("opts", "sysenv", "libpaths", "wd", "lockfilepath", "run")) # needed for mirai call #' @rdname module_teal_lockfile .teal_lockfile_process_invoke <- function(lockfile_path) { - # to ignore running mirai process after app is closed - mirai_obj <- mirai::mirai( - { - options(opts) - do.call(Sys.setenv, sysenv) - .libPaths(libpaths) - setwd(wd) - run(lockfile_path = lockfile_path) - }, - run = .renv_snapshot, - lockfile_path = lockfile_path, - opts = options(), - libpaths = .libPaths(), - sysenv = as.list(Sys.getenv()), - wd = getwd() - ) + mirai_obj <- NULL + process <- shiny::ExtendedTask$new(function() { + m <- mirai::mirai( + { + options(opts) + do.call(Sys.setenv, sysenv) + .libPaths(libpaths) + setwd(wd) + run(lockfile_path = lockfile_path) + }, + run = .renv_snapshot, + lockfile_path = lockfile_path, + opts = options(), + libpaths = .libPaths(), + sysenv = as.list(Sys.getenv()), + wd = getwd() + ) + mirai_obj <<- m + m + }) + shiny::onStop(function() { if (mirai::unresolved(mirai_obj)) { logger::log_debug("Terminating a running lockfile process...") mirai::stop_mirai(mirai_obj) # this doesn't stop running - renv will be created even if session is closed } }) - process <- shiny::ExtendedTask$new(function() mirai_obj) suppressWarnings({ # 'package:stats' may not be available when loading process$invoke() diff --git a/tests/testthat/test-module_teal.R b/tests/testthat/test-module_teal.R index 3927cb2d19..48a2ba3da6 100644 --- a/tests/testthat/test-module_teal.R +++ b/tests/testthat/test-module_teal.R @@ -80,7 +80,7 @@ transform_list <<- list( ) testthat::describe("srv_teal lockfile", { - testthat::it("creation process is invoked and snapshot is copied to teal_app.lock", { + testthat::it("creation process is invoked and snapshot is copied to teal_app.lock and removed after session ended", { withr::with_options( list(teal.renv.enable = TRUE), { From 4834f4eed658ba4f267900fbb4ba8e8e99a3d75f Mon Sep 17 00:00:00 2001 From: go_gonzo Date: Tue, 20 Aug 2024 08:36:47 +0200 Subject: [PATCH 60/85] turnoff renv in tests --- R/zzz.R | 2 ++ tests/testthat/setup-options.R | 30 ++++++++++++++++++++++++------ 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/R/zzz.R b/R/zzz.R index 87ca241f27..57a0e96178 100644 --- a/R/zzz.R +++ b/R/zzz.R @@ -2,6 +2,8 @@ # adapted from https://github.com/r-lib/devtools/blob/master/R/zzz.R teal_default_options <- list( teal.show_js_log = FALSE, + # todo: make below teal.renv.enable = FALSE during run_examples, test_package, r cmd check + # run_examples probably doesn't set any sysvars but maybe detecting run_examples in sys.calls() helps teal.renv.enable = TRUE, shiny.sanitize.errors = FALSE ) diff --git a/tests/testthat/setup-options.R b/tests/testthat/setup-options.R index 1ee67b5a6f..bfedfeb5d7 100644 --- a/tests/testthat/setup-options.R +++ b/tests/testthat/setup-options.R @@ -1,9 +1,27 @@ +# we should't run lockfile process multiple times in tests as it starts background process which is not +# possible to kill once run. It means that background R sessions are being cumulated withr::local_options( - list( - warnPartialMatchDollar = TRUE, - warnPartialMatchArgs = TRUE, - warnPartialMatchAttr = TRUE, - teal.renv.enable = FALSE - ), + list(teal.renv.enable = FALSE), .local_envir = testthat::teardown_env() ) + +# `opts_partial_match_old` is left for exclusions due to partial matching in dependent packages (i.e. not fixable here) +# it might happen that it is not used right now, but it is left for possible future use +# use with: `withr::with_options(opts_partial_match_old, { ... })` inside the test +opts_partial_match_old <- list( + warnPartialMatchDollar = getOption("warnPartialMatchDollar"), + warnPartialMatchArgs = getOption("warnPartialMatchArgs"), + warnPartialMatchAttr = getOption("warnPartialMatchAttr") +) +opts_partial_match_new <- list( + warnPartialMatchDollar = TRUE, + warnPartialMatchArgs = TRUE, + warnPartialMatchAttr = TRUE +) + +if (isFALSE(getFromNamespace("on_cran", "testthat")()) && requireNamespace("withr", quietly = TRUE)) { + withr::local_options( + opts_partial_match_new, + .local_envir = testthat::teardown_env() + ) +} From df91de61d2ec0fed1212225985868aecb88fc8e0 Mon Sep 17 00:00:00 2001 From: go_gonzo Date: Mon, 2 Sep 2024 10:26:19 +0200 Subject: [PATCH 61/85] some spelling fixes --- R/module_teal_lockfile.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/R/module_teal_lockfile.R b/R/module_teal_lockfile.R index 253086fee5..87ac2de184 100644 --- a/R/module_teal_lockfile.R +++ b/R/module_teal_lockfile.R @@ -73,7 +73,7 @@ srv_teal_lockfile <- function(id) { # run renv::snapshot only once per app. Once calculated available for all users if (file.exists(lockfile_path)) { - logger::log_debug("Lockfile have been already created - skipping automatic creation.") + logger::log_debug("Lockfile has been already created - skipping automatic creation.") enable_lockfile_download() return(NULL) } @@ -87,7 +87,7 @@ srv_teal_lockfile <- function(id) { enable_lockfile_download() return(NULL) } else { - warning("lockfile provided through options('teal.renv.lockfile') does not exist.", call. = FALSE) + warning("Lockfile provided through options('teal.renv.lockfile') does not exist.", call. = FALSE) } } From 7b6f822b77697ccc3275bc1d6c72e7c0401d7fa0 Mon Sep 17 00:00:00 2001 From: go_gonzo Date: Mon, 2 Sep 2024 10:45:55 +0200 Subject: [PATCH 62/85] fix verdepcheck --- DESCRIPTION | 4 ++-- tests/testthat/setup-options.R | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index f07ac4b7a4..9407506e18 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -74,14 +74,14 @@ RdMacros: Config/Needs/verdepcheck: rstudio/shiny, insightsengineering/teal.data, insightsengineering/teal.slice, mllg/checkmate, jeroen/jsonlite, r-lib/lifecycle, - daroczig/logger, rstudio/renv, r-lib/rlang, + daroczig/logger, shikokuchuo/mirai, shikokuchuo/nanonext + rstudio/renv, r-lib/rlang, daattali/shinyjs, insightsengineering/teal.code, insightsengineering/teal.logger, insightsengineering/teal.reporter, insightsengineering/teal.widgets, rstudio/bslib, yihui/knitr, bioc::MultiAssayExperiment, r-lib/R6, rstudio/rmarkdown, tidyverse/rvest, rstudio/shinytest2, rstudio/shinyvalidate, r-lib/testthat, r-lib/withr, yaml=vubiostat/r-yaml - daroczig/logger, tidyverse/magrittr, shikokuchuo/mirai, shikokuchuo/nanonext, rstudio/renv, Config/Needs/website: insightsengineering/nesttemplate Encoding: UTF-8 Language: en-US diff --git a/tests/testthat/setup-options.R b/tests/testthat/setup-options.R index bfedfeb5d7..edcc9bff29 100644 --- a/tests/testthat/setup-options.R +++ b/tests/testthat/setup-options.R @@ -1,6 +1,6 @@ -# we should't run lockfile process multiple times in tests as it starts background process which is not -# possible to kill once run. It means that background R sessions are being cumulated withr::local_options( + # we should't run lockfile process multiple times in tests as it starts background process which is not + # possible to kill once run. It means that background R sessions are being cumulated list(teal.renv.enable = FALSE), .local_envir = testthat::teardown_env() ) @@ -14,6 +14,7 @@ opts_partial_match_old <- list( warnPartialMatchAttr = getOption("warnPartialMatchAttr") ) opts_partial_match_new <- list( + teal.renv.enable = FALSE, warnPartialMatchDollar = TRUE, warnPartialMatchArgs = TRUE, warnPartialMatchAttr = TRUE From 6f014096551ab8b183775a18122f4db4766fa391 Mon Sep 17 00:00:00 2001 From: "27856297+dependabot-preview[bot]@users.noreply.github.com" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 5 Sep 2024 11:54:26 +0200 Subject: [PATCH 63/85] wow --- DESCRIPTION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index 79f1ad2e16..0d0a354fa6 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -74,7 +74,7 @@ RdMacros: Config/Needs/verdepcheck: rstudio/shiny, insightsengineering/teal.data, insightsengineering/teal.slice, mllg/checkmate, jeroen/jsonlite, r-lib/lifecycle, - daroczig/logger, shikokuchuo/mirai, shikokuchuo/nanonext + daroczig/logger, shikokuchuo/mirai, shikokuchuo/nanonext, rstudio/renv, r-lib/rlang, daattali/shinyjs, insightsengineering/teal.code, insightsengineering/teal.logger, insightsengineering/teal.reporter, From b6a1dc89baa9eeee4c2009f817b4b6570b652163 Mon Sep 17 00:00:00 2001 From: "27856297+dependabot-preview[bot]@users.noreply.github.com" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 5 Sep 2024 15:23:46 +0200 Subject: [PATCH 64/85] disable lockfile creation when no packages + throw a warning --- DESCRIPTION | 4 ++-- R/module_teal_lockfile.R | 8 ++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 0d0a354fa6..30a53d96d3 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -45,8 +45,6 @@ Imports: lifecycle (>= 0.2.0), logger (>= 0.2.0), methods, - mirai (>= 1.1.1), - renv (>= 1.0.7), rlang (>= 1.0.0), shinyjs, stats, @@ -58,8 +56,10 @@ Imports: Suggests: bslib, knitr (>= 1.42), + mirai (>= 1.1.1), MultiAssayExperiment, R6, + renv (>= 1.0.7), rmarkdown (>= 2.23), rvest, shinytest2, diff --git a/R/module_teal_lockfile.R b/R/module_teal_lockfile.R index 87ac2de184..8d978f48e6 100644 --- a/R/module_teal_lockfile.R +++ b/R/module_teal_lockfile.R @@ -34,6 +34,11 @@ NULL #' @rdname module_teal_lockfile ui_teal_lockfile <- function(id) { + if (!requireNamespace("mirai", quietly = TRUE) || !requireNamespace("renv", quietly = TRUE)) { + warning("lockfile feature disabled. `mirai` and `renv` packages must be installed.") + return(NULL) + } + ns <- NS(id) if (!isTRUE(getOption("teal.renv.enable"))) { return(NULL) @@ -46,6 +51,9 @@ ui_teal_lockfile <- function(id) { #' @rdname module_teal_lockfile srv_teal_lockfile <- function(id) { + if (!requireNamespace(c("mirai", "renv"), quietly = TRUE)) { + return(NULL) + } moduleServer(id, function(input, output, session) { logger::log_debug("Initialize srv_teal_lockfile.") enable_lockfile_download <- function() { From debc4ce4e7bf1acba2ed09554e543a58e5cd0dc5 Mon Sep 17 00:00:00 2001 From: "27856297+dependabot-preview[bot]@users.noreply.github.com" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 5 Sep 2024 15:50:39 +0200 Subject: [PATCH 65/85] - disable and warn when mirai or renv not available --- R/module_teal_lockfile.R | 20 ++++++++++---------- man/module_teal_lockfile.Rd | 3 +++ 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/R/module_teal_lockfile.R b/R/module_teal_lockfile.R index 8d978f48e6..fb45feaceb 100644 --- a/R/module_teal_lockfile.R +++ b/R/module_teal_lockfile.R @@ -34,15 +34,14 @@ NULL #' @rdname module_teal_lockfile ui_teal_lockfile <- function(id) { - if (!requireNamespace("mirai", quietly = TRUE) || !requireNamespace("renv", quietly = TRUE)) { - warning("lockfile feature disabled. `mirai` and `renv` packages must be installed.") + if (!isTRUE(getOption("teal.renv.enable"))) { + return(NULL) + } else if (!.is_lockfile_deps_installed()) { + warning("Lockfile feature disabled. `mirai` and `renv` packages must be installed.") return(NULL) } ns <- NS(id) - if (!isTRUE(getOption("teal.renv.enable"))) { - return(NULL) - } shiny::tagList( tags$span("", id = ns("lockFileStatus")), shinyjs::disabled(downloadLink(ns("lockFileLink"), "Download lockfile")) @@ -51,7 +50,7 @@ ui_teal_lockfile <- function(id) { #' @rdname module_teal_lockfile srv_teal_lockfile <- function(id) { - if (!requireNamespace(c("mirai", "renv"), quietly = TRUE)) { + if (!isTRUE(getOption("teal.renv.enable")) || !.is_lockfile_deps_installed()) { return(NULL) } moduleServer(id, function(input, output, session) { @@ -67,10 +66,6 @@ srv_teal_lockfile <- function(id) { shinyjs::disable("lockFileLink") } - if (!isTRUE(getOption("teal.renv.enable"))) { - return(NULL) - } - lockfile_path <- "teal_app.lock" shiny::onStop(function() { if (file.exists(lockfile_path) && !shiny::isRunning()) { @@ -193,3 +188,8 @@ utils::globalVariables(c("opts", "sysenv", "libpaths", "wd", "lockfilepath", "ru list(out = out, res = res) } + +#' @rdname module_teal_lockfile +.is_lockfile_deps_installed <- function() { + requireNamespace("mirai", quietly = TRUE) && requireNamespace("renv", quietly = TRUE) +} diff --git a/man/module_teal_lockfile.Rd b/man/module_teal_lockfile.Rd index 45ab22abb3..ac3dcc7230 100644 --- a/man/module_teal_lockfile.Rd +++ b/man/module_teal_lockfile.Rd @@ -6,6 +6,7 @@ \alias{srv_teal_lockfile} \alias{.teal_lockfile_process_invoke} \alias{.renv_snapshot} +\alias{.is_lockfile_deps_installed} \title{Generate lockfile for application's environment reproducibility} \usage{ ui_teal_lockfile(id) @@ -15,6 +16,8 @@ srv_teal_lockfile(id) .teal_lockfile_process_invoke(lockfile_path) .renv_snapshot(lockfile_path) + +.is_lockfile_deps_installed() } \arguments{ \item{lockfile_path}{(\code{character}) path to the lockfile (\code{"teal_app.lock"}).} From d1c97352dbece242cf93378ed6e7e199f64b6607 Mon Sep 17 00:00:00 2001 From: "27856297+dependabot-preview[bot]@users.noreply.github.com" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 6 Sep 2024 14:00:45 +0200 Subject: [PATCH 66/85] - change options to follow `teal.lockfile.` pattern - lockfile logic only in the server --- R/module_teal_lockfile.R | 55 ++++++++++++++++--------------- R/zzz.R | 4 +-- man/module_teal_lockfile.Rd | 4 +-- tests/testthat/setup-options.R | 4 +-- tests/testthat/test-module_teal.R | 8 ++--- vignettes/teal-options.Rmd | 10 ++++-- 6 files changed, 45 insertions(+), 40 deletions(-) diff --git a/R/module_teal_lockfile.R b/R/module_teal_lockfile.R index fb45feaceb..e101b7471c 100644 --- a/R/module_teal_lockfile.R +++ b/R/module_teal_lockfile.R @@ -7,9 +7,9 @@ #' #' - User-specified: #' - **Pre-computed lockfile**: Users can provide their own pre-computed lockfile by specifying the path via -#' `teal.renv.lockfile` option. Automatic lockfile computation is skipped in such case. +#' `teal.lockfile.path` option. Automatic lockfile computation is skipped in such case. #' - Automatically computed: -#' - **Working directory lockfile**: If `teal.renv.lockfile` is not set, `teal` will, by default, create an +#' - **Working directory lockfile**: If `teal.lockfile.path` is not set, `teal` will, by default, create an #' `implicit` type lockfile that uses `renv::dependencies()` to detect all R packages in the current project's #' working directory. #' - **`DESCRIPTION`-based lockfile**: To generate a lockfile based on a `DESCRIPTION` file in your working @@ -34,13 +34,6 @@ NULL #' @rdname module_teal_lockfile ui_teal_lockfile <- function(id) { - if (!isTRUE(getOption("teal.renv.enable"))) { - return(NULL) - } else if (!.is_lockfile_deps_installed()) { - warning("Lockfile feature disabled. `mirai` and `renv` packages must be installed.") - return(NULL) - } - ns <- NS(id) shiny::tagList( tags$span("", id = ns("lockFileStatus")), @@ -50,15 +43,22 @@ ui_teal_lockfile <- function(id) { #' @rdname module_teal_lockfile srv_teal_lockfile <- function(id) { - if (!isTRUE(getOption("teal.renv.enable")) || !.is_lockfile_deps_installed()) { - return(NULL) - } moduleServer(id, function(input, output, session) { logger::log_debug("Initialize srv_teal_lockfile.") enable_lockfile_download <- function() { shinyjs::html("lockFileStatus", "Application lockfile ready.") shinyjs::hide("lockFileStatus", anim = TRUE) shinyjs::enable("lockFileLink") + output$lockFileLink <- shiny::downloadHandler( + filename = function() { + "renv.lock" + }, + content = function(file) { + file.copy(lockfile_path, file) + file + }, + contentType = "application/json" + ) } disable_lockfile_download <- function() { warning("Lockfile creation failed.", call. = FALSE) @@ -66,7 +66,20 @@ srv_teal_lockfile <- function(id) { shinyjs::disable("lockFileLink") } + is_lockfile_enabled <- isTRUE(getOption("teal.lockfile.enable")) + user_lockfile <- getOption("teal.lockfile.path", "") lockfile_path <- "teal_app.lock" + + if (!is_lockfile_enabled) { + logger::log_debug("'teal.lockfile.enable' option is set to false. Hiding a lockfile download button.") + shinyjs::hide("lockFileLink") + return(NULL) + } else if (identical(user_lockfile, "") && !.is_lockfile_deps_installed()) { + warning("Automatic lockfile creation disabled. `mirai` and `renv` packages must be installed.") + shinyjs::hide("lockFileLink") + return(NULL) + } + shiny::onStop(function() { if (file.exists(lockfile_path) && !shiny::isRunning()) { logger::log_debug("Removing lockfile after shutting down the app") @@ -81,16 +94,15 @@ srv_teal_lockfile <- function(id) { return(NULL) } + # don't run renv::snapshot when option is set - user_lockfile <- getOption("teal.renv.lockfile", "") if (!identical(user_lockfile, "")) { if (file.exists(user_lockfile)) { file.copy(user_lockfile, lockfile_path) - logger::log_debug('Lockfile set using option "teal.renv.lockfile" - skipping automatic creation.') + logger::log_debug('Lockfile set using option "teal.lockfile.path" - skipping automatic creation.') enable_lockfile_download() - return(NULL) } else { - warning("Lockfile provided through options('teal.renv.lockfile') does not exist.", call. = FALSE) + warning("Lockfile provided through options('teal.lockfile.path') does not exist.", call. = FALSE) } } @@ -120,17 +132,6 @@ srv_teal_lockfile <- function(id) { } }) - output$lockFileLink <- shiny::downloadHandler( - filename = function() { - "renv.lock" - }, - content = function(file) { - file.copy(lockfile_path, file) - file - }, - contentType = "application/json" - ) - NULL }) } diff --git a/R/zzz.R b/R/zzz.R index 57a0e96178..9b324ff5ff 100644 --- a/R/zzz.R +++ b/R/zzz.R @@ -2,9 +2,7 @@ # adapted from https://github.com/r-lib/devtools/blob/master/R/zzz.R teal_default_options <- list( teal.show_js_log = FALSE, - # todo: make below teal.renv.enable = FALSE during run_examples, test_package, r cmd check - # run_examples probably doesn't set any sysvars but maybe detecting run_examples in sys.calls() helps - teal.renv.enable = TRUE, + teal.lockfile.enable = TRUE, shiny.sanitize.errors = FALSE ) diff --git a/man/module_teal_lockfile.Rd b/man/module_teal_lockfile.Rd index ac3dcc7230..e136334785 100644 --- a/man/module_teal_lockfile.Rd +++ b/man/module_teal_lockfile.Rd @@ -35,11 +35,11 @@ Generate lockfile for application's environment reproducibility \item User-specified: \itemize{ \item \strong{Pre-computed lockfile}: Users can provide their own pre-computed lockfile by specifying the path via -\code{teal.renv.lockfile} option. Automatic lockfile computation is skipped in such case. +\code{teal.lockfile.path} option. Automatic lockfile computation is skipped in such case. } \item Automatically computed: \itemize{ -\item \strong{Working directory lockfile}: If \code{teal.renv.lockfile} is not set, \code{teal} will, by default, create an +\item \strong{Working directory lockfile}: If \code{teal.lockfile.path} is not set, \code{teal} will, by default, create an \code{implicit} type lockfile that uses \code{renv::dependencies()} to detect all R packages in the current project's working directory. \item \strong{\code{DESCRIPTION}-based lockfile}: To generate a lockfile based on a \code{DESCRIPTION} file in your working diff --git a/tests/testthat/setup-options.R b/tests/testthat/setup-options.R index edcc9bff29..5510b44552 100644 --- a/tests/testthat/setup-options.R +++ b/tests/testthat/setup-options.R @@ -1,7 +1,7 @@ withr::local_options( # we should't run lockfile process multiple times in tests as it starts background process which is not # possible to kill once run. It means that background R sessions are being cumulated - list(teal.renv.enable = FALSE), + list(teal.lockfile.enable = FALSE), .local_envir = testthat::teardown_env() ) @@ -14,7 +14,7 @@ opts_partial_match_old <- list( warnPartialMatchAttr = getOption("warnPartialMatchAttr") ) opts_partial_match_new <- list( - teal.renv.enable = FALSE, + teal.lockfile.enable = FALSE, warnPartialMatchDollar = TRUE, warnPartialMatchArgs = TRUE, warnPartialMatchAttr = TRUE diff --git a/tests/testthat/test-module_teal.R b/tests/testthat/test-module_teal.R index 4bfc5a298b..0475a8c4b8 100644 --- a/tests/testthat/test-module_teal.R +++ b/tests/testthat/test-module_teal.R @@ -68,7 +68,7 @@ transform_list <<- list( testthat::describe("srv_teal lockfile", { testthat::it("creation process is invoked and snapshot is copied to teal_app.lock and removed after session ended", { withr::with_options( - list(teal.renv.enable = TRUE), + list(teal.lockfile.enable = TRUE), { renv_filename <- "teal_app.lock" shiny::testServer( @@ -91,13 +91,13 @@ testthat::describe("srv_teal lockfile", { } ) }) - testthat::it("is copied from option-teal.renv.lockfile and then removed from an app directory", { + testthat::it("is copied from option-teal.lockfile.path and then removed from an app directory", { temp_lockfile_mock <- tempfile() writeLines("test", temp_lockfile_mock) withr::with_options( list( - teal.renv.enable = TRUE, - teal.renv.lockfile = temp_lockfile_mock + teal.lockfile.enable = TRUE, + teal.lockfile.path = temp_lockfile_mock ), { renv_filename <- "teal_app.lock" diff --git a/vignettes/teal-options.Rmd b/vignettes/teal-options.Rmd index 99accfa5a6..e079af6e6a 100644 --- a/vignettes/teal-options.Rmd +++ b/vignettes/teal-options.Rmd @@ -84,10 +84,16 @@ This indicates whether to print the `JavaScript` console logs to the `R` console Default: `FALSE`. -### `teal.renv.lockfile` (`character`) +### `teal.lockfile.enable` (`logical`) -The path to the pre-computed `renv` lockfile that will be shared through teal app. To read more about lockfile usage creation check `?teal::teal_lockfile`. +This enables app users to download `renv` lockfile by clicking "download lockfile" in the footer. If +`teal.lockfile.path` is not specified then lockfile is created automatically by `teal`. To read more about +lockfile usage creation check `?teal::teal_lockfile`. +### `teal.lockfile.path` (`character`) + +The path to the pre-computed `renv` lockfile linked by app developer. Lockfile will be available to download if +`teal.lockfile.enable` is set to `TRUE`. # Deprecated options From c0378c847434513af6e9c9aba6acc8bad557b456 Mon Sep 17 00:00:00 2001 From: "27856297+dependabot-preview[bot]@users.noreply.github.com" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 10 Sep 2024 10:03:18 +0200 Subject: [PATCH 67/85] make code more readible --- R/module_teal_lockfile.R | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/R/module_teal_lockfile.R b/R/module_teal_lockfile.R index e101b7471c..ddf7ca794b 100644 --- a/R/module_teal_lockfile.R +++ b/R/module_teal_lockfile.R @@ -66,39 +66,39 @@ srv_teal_lockfile <- function(id) { shinyjs::disable("lockFileLink") } - is_lockfile_enabled <- isTRUE(getOption("teal.lockfile.enable")) - user_lockfile <- getOption("teal.lockfile.path", "") + shiny::onStop(function() { + if (file.exists(lockfile_path) && !shiny::isRunning()) { + logger::log_debug("Removing lockfile after shutting down the app") + file.remove(lockfile_path) + } + }) + lockfile_path <- "teal_app.lock" + is_lockfile_enabled <- isTRUE(getOption("teal.lockfile.enable")) + user_lockfile_path <- getOption("teal.lockfile.path", default = "") + is_user_lockfile_set <- !identical(user_lockfile_path, "") if (!is_lockfile_enabled) { logger::log_debug("'teal.lockfile.enable' option is set to false. Hiding a lockfile download button.") shinyjs::hide("lockFileLink") return(NULL) - } else if (identical(user_lockfile, "") && !.is_lockfile_deps_installed()) { - warning("Automatic lockfile creation disabled. `mirai` and `renv` packages must be installed.") - shinyjs::hide("lockFileLink") - return(NULL) } - shiny::onStop(function() { - if (file.exists(lockfile_path) && !shiny::isRunning()) { - logger::log_debug("Removing lockfile after shutting down the app") - file.remove(lockfile_path) - } - }) - - # run renv::snapshot only once per app. Once calculated available for all users if (file.exists(lockfile_path)) { - logger::log_debug("Lockfile has been already created - skipping automatic creation.") + logger::log_debug("Lockfile has already been created for this app - skipping automatic creation.") enable_lockfile_download() return(NULL) } + if (!is_user_lockfile_set && !.is_lockfile_deps_installed()) { + warning("Automatic lockfile creation disabled. `mirai` and `renv` packages must be installed.") + shinyjs::hide("lockFileLink") + return(NULL) + } - # don't run renv::snapshot when option is set - if (!identical(user_lockfile, "")) { - if (file.exists(user_lockfile)) { - file.copy(user_lockfile, lockfile_path) + if (is_user_lockfile_set) { + if (file.exists(user_lockfile_path)) { + file.copy(user_lockfile_path, lockfile_path) logger::log_debug('Lockfile set using option "teal.lockfile.path" - skipping automatic creation.') enable_lockfile_download() } else { From ec73a04a6064c9b516491bef0a309da2447908b0 Mon Sep 17 00:00:00 2001 From: "27856297+dependabot-preview[bot]@users.noreply.github.com" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 17 Sep 2024 12:46:47 +0200 Subject: [PATCH 68/85] - remove `teal_app.lock` occurrence from the docs - remove unnecessary from testthat/setup-option.R --- R/module_teal_lockfile.R | 2 +- man/module_teal_lockfile.Rd | 2 +- tests/testthat/setup-options.R | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/R/module_teal_lockfile.R b/R/module_teal_lockfile.R index ddf7ca794b..0365924460 100644 --- a/R/module_teal_lockfile.R +++ b/R/module_teal_lockfile.R @@ -1,6 +1,6 @@ #' Generate lockfile for application's environment reproducibility #' -#' @param lockfile_path (`character`) path to the lockfile (`"teal_app.lock"`). +#' @param lockfile_path (`character`) path to the lockfile. #' #' @section Different ways of creating lockfile: #' `teal` leverages [renv::snapshot()], which offers multiple methods for lockfile creation. diff --git a/man/module_teal_lockfile.Rd b/man/module_teal_lockfile.Rd index e136334785..e3c209d787 100644 --- a/man/module_teal_lockfile.Rd +++ b/man/module_teal_lockfile.Rd @@ -20,7 +20,7 @@ srv_teal_lockfile(id) .is_lockfile_deps_installed() } \arguments{ -\item{lockfile_path}{(\code{character}) path to the lockfile (\code{"teal_app.lock"}).} +\item{lockfile_path}{(\code{character}) path to the lockfile.} } \value{ \code{NULL} diff --git a/tests/testthat/setup-options.R b/tests/testthat/setup-options.R index 5510b44552..e923a60c7f 100644 --- a/tests/testthat/setup-options.R +++ b/tests/testthat/setup-options.R @@ -14,7 +14,6 @@ opts_partial_match_old <- list( warnPartialMatchAttr = getOption("warnPartialMatchAttr") ) opts_partial_match_new <- list( - teal.lockfile.enable = FALSE, warnPartialMatchDollar = TRUE, warnPartialMatchArgs = TRUE, warnPartialMatchAttr = TRUE From 82bb3fb8f6c78477018e9a8dad160ddf6a1882fa Mon Sep 17 00:00:00 2001 From: m7pr Date: Wed, 18 Sep 2024 10:54:40 +0200 Subject: [PATCH 69/85] documentation updates --- R/module_teal_lockfile.R | 4 ++-- man/module_teal_lockfile.Rd | 2 +- vignettes/teal-options.Rmd | 13 ++++++++----- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/R/module_teal_lockfile.R b/R/module_teal_lockfile.R index 0365924460..8ff8b087d5 100644 --- a/R/module_teal_lockfile.R +++ b/R/module_teal_lockfile.R @@ -20,7 +20,7 @@ #' `renv::settings$snapshot.type("custom")` and configure the `renv.snapshot.filter` option. #' #' @section lockfile usage: -#' After creating the lockfile, you can restore the application environment using `renv::restore()`. +#' After creating the lockfile, you can restore the application's environment using `renv::restore()`. #' #' @seealso [renv::snapshot()], [renv::restore()]. #' @@ -79,7 +79,7 @@ srv_teal_lockfile <- function(id) { is_user_lockfile_set <- !identical(user_lockfile_path, "") if (!is_lockfile_enabled) { - logger::log_debug("'teal.lockfile.enable' option is set to false. Hiding a lockfile download button.") + logger::log_debug("'teal.lockfile.enable' option is set to FALSE. Hiding a lockfile download button.") shinyjs::hide("lockFileLink") return(NULL) } diff --git a/man/module_teal_lockfile.Rd b/man/module_teal_lockfile.Rd index e3c209d787..2dfeac7e75 100644 --- a/man/module_teal_lockfile.Rd +++ b/man/module_teal_lockfile.Rd @@ -54,7 +54,7 @@ directory, set \code{renv::settings$snapshot.type("explicit")}. The naming conve \section{lockfile usage}{ -After creating the lockfile, you can restore the application environment using \code{renv::restore()}. +After creating the lockfile, you can restore the application's environment using \code{renv::restore()}. } \seealso{ diff --git a/vignettes/teal-options.Rmd b/vignettes/teal-options.Rmd index e079af6e6a..f0a643a74a 100644 --- a/vignettes/teal-options.Rmd +++ b/vignettes/teal-options.Rmd @@ -86,14 +86,17 @@ Default: `FALSE`. ### `teal.lockfile.enable` (`logical`) -This enables app users to download `renv` lockfile by clicking "download lockfile" in the footer. If -`teal.lockfile.path` is not specified then lockfile is created automatically by `teal`. To read more about -lockfile usage creation check `?teal::teal_lockfile`. +This enables app users to download `renv` lockfile by clicking "download lockfile" in the footer. +If `teal.lockfile.path` is not specified then lockfile is created automatically by `teal`. + +To read more about lockfile usage creation check `?teal::module_teal_lockfile`. ### `teal.lockfile.path` (`character`) -The path to the pre-computed `renv` lockfile linked by app developer. Lockfile will be available to download if -`teal.lockfile.enable` is set to `TRUE`. +The path to the pre-computed `renv` lockfile linked by app developer. +Lockfile will be available to download if `teal.lockfile.enable` is set to `TRUE`. + +To read more about lockfile usage creation check `?teal::module_teal_lockfile`. # Deprecated options From 761dd68e10426c451ba9eb1c0b8d2f40b0ead583 Mon Sep 17 00:00:00 2001 From: Marcin <133694481+m7pr@users.noreply.github.com> Date: Thu, 19 Sep 2024 10:04:38 +0200 Subject: [PATCH 70/85] disable lockfile generation in `callr`, `testthat::tests` and `R CMD CHECK` (#1346) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Part of https://github.com/insightsengineering/teal/pull/1263 and closes https://github.com/insightsengineering/teal/issues/1276 Local results of `shinytest2` tests ```r ══ Results ══════════════════════════════════════════════════════════════════════════ Duration: 540.8 s ── Skipped tests (6) ──────────────────────────────────────────────────────────────── • need a fix in a .slicesGlobal (1): test-module_teal.R:1139:11 • todo (5): test-module_teal.R:1404:7, test-module_teal.R:1411:5, test-module_teal.R:1414:5, test-module_teal.R:1673:5, test-module_teal.R:1731:5 [ FAIL 0 | WARN 0 | SKIP 6 | PASS 515 ] ``` --- R/module_teal_lockfile.R | 18 +++++++++++++++++- R/zzz.R | 2 +- tests/testthat/setup-options.R | 6 ------ 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/R/module_teal_lockfile.R b/R/module_teal_lockfile.R index 8ff8b087d5..43c9816dd1 100644 --- a/R/module_teal_lockfile.R +++ b/R/module_teal_lockfile.R @@ -74,7 +74,7 @@ srv_teal_lockfile <- function(id) { }) lockfile_path <- "teal_app.lock" - is_lockfile_enabled <- isTRUE(getOption("teal.lockfile.enable")) + is_lockfile_enabled <- .is_lockfile_enabled() user_lockfile_path <- getOption("teal.lockfile.path", default = "") is_user_lockfile_set <- !identical(user_lockfile_path, "") @@ -194,3 +194,19 @@ utils::globalVariables(c("opts", "sysenv", "libpaths", "wd", "lockfilepath", "ru .is_lockfile_deps_installed <- function() { requireNamespace("mirai", quietly = TRUE) && requireNamespace("renv", quietly = TRUE) } + +#' @rdname module_teal_lockfile +.is_lockfile_enabled <- function() { + if (isTRUE(getOption("teal.lockfile.enable"))) { + return(TRUE) + } else { + !( + identical(Sys.getenv("CALLR_IS_RUNNING"), "true") || # inside callr process + identical(Sys.getenv("TESTTHAT"), "true") || # inside devtools::test + !identical(Sys.getenv("QUARTO_PROJECT_ROOT"), "") || # inside Quarto process + ( + ("CheckExEnv" %in% search()) || any(c("_R_CHECK_TIMINGS_", "_R_CHECK_LICENSE_") %in% names(Sys.getenv())) + ) # inside R CMD CHECK + ) + } +} diff --git a/R/zzz.R b/R/zzz.R index 9b324ff5ff..fcab860a8a 100644 --- a/R/zzz.R +++ b/R/zzz.R @@ -1,8 +1,8 @@ .onLoad <- function(libname, pkgname) { # adapted from https://github.com/r-lib/devtools/blob/master/R/zzz.R + teal_default_options <- list( teal.show_js_log = FALSE, - teal.lockfile.enable = TRUE, shiny.sanitize.errors = FALSE ) diff --git a/tests/testthat/setup-options.R b/tests/testthat/setup-options.R index e923a60c7f..40d3949a8c 100644 --- a/tests/testthat/setup-options.R +++ b/tests/testthat/setup-options.R @@ -1,9 +1,3 @@ -withr::local_options( - # we should't run lockfile process multiple times in tests as it starts background process which is not - # possible to kill once run. It means that background R sessions are being cumulated - list(teal.lockfile.enable = FALSE), - .local_envir = testthat::teardown_env() -) # `opts_partial_match_old` is left for exclusions due to partial matching in dependent packages (i.e. not fixable here) # it might happen that it is not used right now, but it is left for possible future use From 240cbb6e4b976e4a3dd1b22fe5b7d03e6f0218bf Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 19 Sep 2024 08:06:50 +0000 Subject: [PATCH 71/85] [skip style] [skip vbump] Restyle files --- R/module_teal_lockfile.R | 10 +++++----- tests/testthat/setup-options.R | 1 - 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/R/module_teal_lockfile.R b/R/module_teal_lockfile.R index 43c9816dd1..8da5956fda 100644 --- a/R/module_teal_lockfile.R +++ b/R/module_teal_lockfile.R @@ -202,11 +202,11 @@ utils::globalVariables(c("opts", "sysenv", "libpaths", "wd", "lockfilepath", "ru } else { !( identical(Sys.getenv("CALLR_IS_RUNNING"), "true") || # inside callr process - identical(Sys.getenv("TESTTHAT"), "true") || # inside devtools::test - !identical(Sys.getenv("QUARTO_PROJECT_ROOT"), "") || # inside Quarto process - ( - ("CheckExEnv" %in% search()) || any(c("_R_CHECK_TIMINGS_", "_R_CHECK_LICENSE_") %in% names(Sys.getenv())) - ) # inside R CMD CHECK + identical(Sys.getenv("TESTTHAT"), "true") || # inside devtools::test + !identical(Sys.getenv("QUARTO_PROJECT_ROOT"), "") || # inside Quarto process + ( + ("CheckExEnv" %in% search()) || any(c("_R_CHECK_TIMINGS_", "_R_CHECK_LICENSE_") %in% names(Sys.getenv())) + ) # inside R CMD CHECK ) } } diff --git a/tests/testthat/setup-options.R b/tests/testthat/setup-options.R index 40d3949a8c..78be1f9b5f 100644 --- a/tests/testthat/setup-options.R +++ b/tests/testthat/setup-options.R @@ -1,4 +1,3 @@ - # `opts_partial_match_old` is left for exclusions due to partial matching in dependent packages (i.e. not fixable here) # it might happen that it is not used right now, but it is left for possible future use # use with: `withr::with_options(opts_partial_match_old, { ... })` inside the test From 41ba2cbe26affb5b77bfb62eca3d26966768afa7 Mon Sep 17 00:00:00 2001 From: "27856297+dependabot-preview[bot]@users.noreply.github.com" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 19 Sep 2024 08:13:33 +0000 Subject: [PATCH 72/85] [skip roxygen] [skip vbump] Roxygen Man Pages Auto Update --- man/module_teal_lockfile.Rd | 3 +++ 1 file changed, 3 insertions(+) diff --git a/man/module_teal_lockfile.Rd b/man/module_teal_lockfile.Rd index 2dfeac7e75..eb7b1f179f 100644 --- a/man/module_teal_lockfile.Rd +++ b/man/module_teal_lockfile.Rd @@ -7,6 +7,7 @@ \alias{.teal_lockfile_process_invoke} \alias{.renv_snapshot} \alias{.is_lockfile_deps_installed} +\alias{.is_lockfile_enabled} \title{Generate lockfile for application's environment reproducibility} \usage{ ui_teal_lockfile(id) @@ -18,6 +19,8 @@ srv_teal_lockfile(id) .renv_snapshot(lockfile_path) .is_lockfile_deps_installed() + +.is_lockfile_enabled() } \arguments{ \item{lockfile_path}{(\code{character}) path to the lockfile.} From f76a30ff4403e45e5ff18fcc9ccea05b84d00d72 Mon Sep 17 00:00:00 2001 From: "27856297+dependabot-preview[bot]@users.noreply.github.com" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 19 Sep 2024 10:35:19 +0200 Subject: [PATCH 73/85] empty From f4e268f25fa1d1ea8358058a363cb74f330fcea8 Mon Sep 17 00:00:00 2001 From: m7pr Date: Thu, 19 Sep 2024 16:00:50 +0200 Subject: [PATCH 74/85] introduce teal.lockfile.mode --- R/module_teal_lockfile.R | 45 ++++++++++++++++++------------- R/zzz.R | 1 + tests/testthat/test-module_teal.R | 4 +-- vignettes/teal-options.Rmd | 18 ++++++++++--- 4 files changed, 43 insertions(+), 25 deletions(-) diff --git a/R/module_teal_lockfile.R b/R/module_teal_lockfile.R index 8da5956fda..376541c0e8 100644 --- a/R/module_teal_lockfile.R +++ b/R/module_teal_lockfile.R @@ -74,12 +74,15 @@ srv_teal_lockfile <- function(id) { }) lockfile_path <- "teal_app.lock" - is_lockfile_enabled <- .is_lockfile_enabled() + mode <- getOption("teal.lockfile.mode", default = "") user_lockfile_path <- getOption("teal.lockfile.path", default = "") - is_user_lockfile_set <- !identical(user_lockfile_path, "") - if (!is_lockfile_enabled) { - logger::log_debug("'teal.lockfile.enable' option is set to FALSE. Hiding a lockfile download button.") + if (!(mode %in% c("auto", "true", "false", "user"))) { + stop("'teal.lockfile.mode' option can only be one of \"auto\", \"true\", \"false\", or \"user\". ") + } + + if (mode == "false") { + logger::log_debug("'teal.lockfile.mode' option is set to 'false'. Hiding lockfile download button.") shinyjs::hide("lockFileLink") return(NULL) } @@ -90,20 +93,30 @@ srv_teal_lockfile <- function(id) { return(NULL) } - if (!is_user_lockfile_set && !.is_lockfile_deps_installed()) { + if (mode %in% c("auto", "true") && !.is_lockfile_deps_installed()) { warning("Automatic lockfile creation disabled. `mirai` and `renv` packages must be installed.") shinyjs::hide("lockFileLink") return(NULL) } - if (is_user_lockfile_set) { + if (mode == "auto" && .is_disabled_lockfile_scenario()) { + logger::log_debug( + "Automatic lockfile creation disabled. Execution scenario satisfies teal:::.is_disabled_lockfile_scenario()." + ) + shinyjs::hide("lockFileLink") + return(NULL) + } + + if (mode == "user") { if (file.exists(user_lockfile_path)) { file.copy(user_lockfile_path, lockfile_path) logger::log_debug('Lockfile set using option "teal.lockfile.path" - skipping automatic creation.') enable_lockfile_download() } else { warning("Lockfile provided through options('teal.lockfile.path') does not exist.", call. = FALSE) + shinyjs::hide("lockFileLink") } + return(NULL) } # - Will be run only if the lockfile doesn't exist (see the if-s above) @@ -196,17 +209,11 @@ utils::globalVariables(c("opts", "sysenv", "libpaths", "wd", "lockfilepath", "ru } #' @rdname module_teal_lockfile -.is_lockfile_enabled <- function() { - if (isTRUE(getOption("teal.lockfile.enable"))) { - return(TRUE) - } else { - !( - identical(Sys.getenv("CALLR_IS_RUNNING"), "true") || # inside callr process - identical(Sys.getenv("TESTTHAT"), "true") || # inside devtools::test - !identical(Sys.getenv("QUARTO_PROJECT_ROOT"), "") || # inside Quarto process - ( - ("CheckExEnv" %in% search()) || any(c("_R_CHECK_TIMINGS_", "_R_CHECK_LICENSE_") %in% names(Sys.getenv())) - ) # inside R CMD CHECK - ) - } +.is_disabled_lockfile_scenario <- function() { + identical(Sys.getenv("CALLR_IS_RUNNING"), "true") || # inside callr process + identical(Sys.getenv("TESTTHAT"), "true") || # inside devtools::test + !identical(Sys.getenv("QUARTO_PROJECT_ROOT"), "") || # inside Quarto process + ( + ("CheckExEnv" %in% search()) || any(c("_R_CHECK_TIMINGS_", "_R_CHECK_LICENSE_") %in% names(Sys.getenv())) + ) # inside R CMD CHECK } diff --git a/R/zzz.R b/R/zzz.R index fcab860a8a..a991d041f2 100644 --- a/R/zzz.R +++ b/R/zzz.R @@ -3,6 +3,7 @@ teal_default_options <- list( teal.show_js_log = FALSE, + teal.lockfile.mode = "auto", shiny.sanitize.errors = FALSE ) diff --git a/tests/testthat/test-module_teal.R b/tests/testthat/test-module_teal.R index 0475a8c4b8..50e997e8e4 100644 --- a/tests/testthat/test-module_teal.R +++ b/tests/testthat/test-module_teal.R @@ -68,7 +68,7 @@ transform_list <<- list( testthat::describe("srv_teal lockfile", { testthat::it("creation process is invoked and snapshot is copied to teal_app.lock and removed after session ended", { withr::with_options( - list(teal.lockfile.enable = TRUE), + list(teal.lockfile.mode = 'true'), { renv_filename <- "teal_app.lock" shiny::testServer( @@ -96,7 +96,7 @@ testthat::describe("srv_teal lockfile", { writeLines("test", temp_lockfile_mock) withr::with_options( list( - teal.lockfile.enable = TRUE, + teal.lockfile.mode = 'auto', teal.lockfile.path = temp_lockfile_mock ), { diff --git a/vignettes/teal-options.Rmd b/vignettes/teal-options.Rmd index f0a643a74a..60d7e5d7f6 100644 --- a/vignettes/teal-options.Rmd +++ b/vignettes/teal-options.Rmd @@ -84,17 +84,27 @@ This indicates whether to print the `JavaScript` console logs to the `R` console Default: `FALSE`. -### `teal.lockfile.enable` (`logical`) +### `teal.lockfile.mode` (`character`) -This enables app users to download `renv` lockfile by clicking "download lockfile" in the footer. -If `teal.lockfile.path` is not specified then lockfile is created automatically by `teal`. +This enables app users to download `renv` lockfile by clicking `"download lockfile"` in the footer. + +Values: + +* `"auto"` - auto detect whether to compute `lockfile` +* `"true"` - compute `lockfile` and show `"download lockfile"` in the footer +* `"false"` - do not compute `lockfile` and do not show `"download lockfile"` in the footer +* `"user"` - use pre-computed `lockfile` specified in `teal.lockfile.path` and show `"download lockfile"` in the footer + + +Default: `"auto"`. To read more about lockfile usage creation check `?teal::module_teal_lockfile`. ### `teal.lockfile.path` (`character`) The path to the pre-computed `renv` lockfile linked by app developer. -Lockfile will be available to download if `teal.lockfile.enable` is set to `TRUE`. +Used when `teal.lockfile.enable` is set to `"user"`. +If files does not exist, the warning is thrown and `"download lockfile"` in the footer is not shown. To read more about lockfile usage creation check `?teal::module_teal_lockfile`. From dfbcfca370319f04cb4bf3c36ef9032050313eb1 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 19 Sep 2024 14:03:14 +0000 Subject: [PATCH 75/85] [skip style] [skip vbump] Restyle files --- R/module_teal_lockfile.R | 10 +++++----- tests/testthat/test-module_teal.R | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/R/module_teal_lockfile.R b/R/module_teal_lockfile.R index 376541c0e8..4eb3c88a0c 100644 --- a/R/module_teal_lockfile.R +++ b/R/module_teal_lockfile.R @@ -211,9 +211,9 @@ utils::globalVariables(c("opts", "sysenv", "libpaths", "wd", "lockfilepath", "ru #' @rdname module_teal_lockfile .is_disabled_lockfile_scenario <- function() { identical(Sys.getenv("CALLR_IS_RUNNING"), "true") || # inside callr process - identical(Sys.getenv("TESTTHAT"), "true") || # inside devtools::test - !identical(Sys.getenv("QUARTO_PROJECT_ROOT"), "") || # inside Quarto process - ( - ("CheckExEnv" %in% search()) || any(c("_R_CHECK_TIMINGS_", "_R_CHECK_LICENSE_") %in% names(Sys.getenv())) - ) # inside R CMD CHECK + identical(Sys.getenv("TESTTHAT"), "true") || # inside devtools::test + !identical(Sys.getenv("QUARTO_PROJECT_ROOT"), "") || # inside Quarto process + ( + ("CheckExEnv" %in% search()) || any(c("_R_CHECK_TIMINGS_", "_R_CHECK_LICENSE_") %in% names(Sys.getenv())) + ) # inside R CMD CHECK } diff --git a/tests/testthat/test-module_teal.R b/tests/testthat/test-module_teal.R index 50e997e8e4..825019e61d 100644 --- a/tests/testthat/test-module_teal.R +++ b/tests/testthat/test-module_teal.R @@ -68,7 +68,7 @@ transform_list <<- list( testthat::describe("srv_teal lockfile", { testthat::it("creation process is invoked and snapshot is copied to teal_app.lock and removed after session ended", { withr::with_options( - list(teal.lockfile.mode = 'true'), + list(teal.lockfile.mode = "true"), { renv_filename <- "teal_app.lock" shiny::testServer( @@ -96,7 +96,7 @@ testthat::describe("srv_teal lockfile", { writeLines("test", temp_lockfile_mock) withr::with_options( list( - teal.lockfile.mode = 'auto', + teal.lockfile.mode = "auto", teal.lockfile.path = temp_lockfile_mock ), { From 392437d86fcdc5ccbe07be8e2d2e2f8dca57e8ed Mon Sep 17 00:00:00 2001 From: "27856297+dependabot-preview[bot]@users.noreply.github.com" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 19 Sep 2024 14:12:42 +0000 Subject: [PATCH 76/85] [skip roxygen] [skip vbump] Roxygen Man Pages Auto Update --- man/module_teal_lockfile.Rd | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/man/module_teal_lockfile.Rd b/man/module_teal_lockfile.Rd index eb7b1f179f..17e4aa357e 100644 --- a/man/module_teal_lockfile.Rd +++ b/man/module_teal_lockfile.Rd @@ -7,7 +7,7 @@ \alias{.teal_lockfile_process_invoke} \alias{.renv_snapshot} \alias{.is_lockfile_deps_installed} -\alias{.is_lockfile_enabled} +\alias{.is_disabled_lockfile_scenario} \title{Generate lockfile for application's environment reproducibility} \usage{ ui_teal_lockfile(id) @@ -20,7 +20,7 @@ srv_teal_lockfile(id) .is_lockfile_deps_installed() -.is_lockfile_enabled() +.is_disabled_lockfile_scenario() } \arguments{ \item{lockfile_path}{(\code{character}) path to the lockfile.} From dc9687d732c7cb77ccdd3dd9e64c854e3f9182ef Mon Sep 17 00:00:00 2001 From: Marcin <133694481+m7pr@users.noreply.github.com> Date: Thu, 19 Sep 2024 16:59:38 +0200 Subject: [PATCH 77/85] Update tests/testthat/test-module_teal.R Signed-off-by: Marcin <133694481+m7pr@users.noreply.github.com> --- tests/testthat/test-module_teal.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/testthat/test-module_teal.R b/tests/testthat/test-module_teal.R index 825019e61d..c9853c26b9 100644 --- a/tests/testthat/test-module_teal.R +++ b/tests/testthat/test-module_teal.R @@ -96,7 +96,7 @@ testthat::describe("srv_teal lockfile", { writeLines("test", temp_lockfile_mock) withr::with_options( list( - teal.lockfile.mode = "auto", + teal.lockfile.mode = "user", teal.lockfile.path = temp_lockfile_mock ), { From 5bdd79ca3139b9328ddfbc4c0b3faf78bc224841 Mon Sep 17 00:00:00 2001 From: m7pr Date: Thu, 19 Sep 2024 20:07:06 +0200 Subject: [PATCH 78/85] @pawelru and @gogonzo suggestions --- R/module_teal_lockfile.R | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/R/module_teal_lockfile.R b/R/module_teal_lockfile.R index 4eb3c88a0c..0424bbc21a 100644 --- a/R/module_teal_lockfile.R +++ b/R/module_teal_lockfile.R @@ -63,7 +63,7 @@ srv_teal_lockfile <- function(id) { disable_lockfile_download <- function() { warning("Lockfile creation failed.", call. = FALSE) shinyjs::html("lockFileStatus", "Lockfile creation failed.") - shinyjs::disable("lockFileLink") + shinyjs::hide("lockFileLink") } shiny::onStop(function() { @@ -87,15 +87,21 @@ srv_teal_lockfile <- function(id) { return(NULL) } - if (file.exists(lockfile_path)) { - logger::log_debug("Lockfile has already been created for this app - skipping automatic creation.") - enable_lockfile_download() + if (mode == "user") { + if (file.exists(user_lockfile_path)) { + file.copy(user_lockfile_path, lockfile_path) + logger::log_debug('Lockfile set using option "teal.lockfile.path" - skipping automatic creation.') + enable_lockfile_download() + } else { + warning("Lockfile provided through options('teal.lockfile.path') does not exist.", call. = FALSE) + shinyjs::hide("lockFileLink") + } return(NULL) } - if (mode %in% c("auto", "true") && !.is_lockfile_deps_installed()) { - warning("Automatic lockfile creation disabled. `mirai` and `renv` packages must be installed.") - shinyjs::hide("lockFileLink") + if (file.exists(lockfile_path)) { + logger::log_debug("Lockfile has already been created for this app - skipping automatic creation.") + enable_lockfile_download() return(NULL) } @@ -107,15 +113,9 @@ srv_teal_lockfile <- function(id) { return(NULL) } - if (mode == "user") { - if (file.exists(user_lockfile_path)) { - file.copy(user_lockfile_path, lockfile_path) - logger::log_debug('Lockfile set using option "teal.lockfile.path" - skipping automatic creation.') - enable_lockfile_download() - } else { - warning("Lockfile provided through options('teal.lockfile.path') does not exist.", call. = FALSE) - shinyjs::hide("lockFileLink") - } + if (.is_lockfile_deps_installed()) { + warning("Automatic lockfile creation disabled. `mirai` and `renv` packages must be installed.") + shinyjs::hide("lockFileLink") return(NULL) } From 0effb5132db951e8928a44b394138c8e42f0aa3d Mon Sep 17 00:00:00 2001 From: m7pr Date: Thu, 19 Sep 2024 20:10:12 +0200 Subject: [PATCH 79/85] typo --- R/module_teal_lockfile.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/module_teal_lockfile.R b/R/module_teal_lockfile.R index 0424bbc21a..aa85dce29a 100644 --- a/R/module_teal_lockfile.R +++ b/R/module_teal_lockfile.R @@ -113,7 +113,7 @@ srv_teal_lockfile <- function(id) { return(NULL) } - if (.is_lockfile_deps_installed()) { + if (!.is_lockfile_deps_installed()) { warning("Automatic lockfile creation disabled. `mirai` and `renv` packages must be installed.") shinyjs::hide("lockFileLink") return(NULL) From 139890a53ef49017c176dae59170c3df75585ad4 Mon Sep 17 00:00:00 2001 From: m7pr Date: Thu, 19 Sep 2024 20:13:50 +0200 Subject: [PATCH 80/85] move user_lockfile_path under "user" condition --- R/module_teal_lockfile.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/module_teal_lockfile.R b/R/module_teal_lockfile.R index aa85dce29a..8682a37ec3 100644 --- a/R/module_teal_lockfile.R +++ b/R/module_teal_lockfile.R @@ -75,7 +75,6 @@ srv_teal_lockfile <- function(id) { lockfile_path <- "teal_app.lock" mode <- getOption("teal.lockfile.mode", default = "") - user_lockfile_path <- getOption("teal.lockfile.path", default = "") if (!(mode %in% c("auto", "true", "false", "user"))) { stop("'teal.lockfile.mode' option can only be one of \"auto\", \"true\", \"false\", or \"user\". ") @@ -88,6 +87,7 @@ srv_teal_lockfile <- function(id) { } if (mode == "user") { + user_lockfile_path <- getOption("teal.lockfile.path", default = "") if (file.exists(user_lockfile_path)) { file.copy(user_lockfile_path, lockfile_path) logger::log_debug('Lockfile set using option "teal.lockfile.path" - skipping automatic creation.') From eeb6d1a98137e5c2c3aefd2cfc996f91015ebbc4 Mon Sep 17 00:00:00 2001 From: m7pr Date: Fri, 20 Sep 2024 11:06:21 +0200 Subject: [PATCH 81/85] remove user option for lockfile creation --- R/module_teal_lockfile.R | 17 ++--------------- tests/testthat/test-module_teal.R | 17 +++++++---------- vignettes/teal-options.Rmd | 9 --------- 3 files changed, 9 insertions(+), 34 deletions(-) diff --git a/R/module_teal_lockfile.R b/R/module_teal_lockfile.R index 8682a37ec3..a3b60637eb 100644 --- a/R/module_teal_lockfile.R +++ b/R/module_teal_lockfile.R @@ -76,8 +76,8 @@ srv_teal_lockfile <- function(id) { lockfile_path <- "teal_app.lock" mode <- getOption("teal.lockfile.mode", default = "") - if (!(mode %in% c("auto", "true", "false", "user"))) { - stop("'teal.lockfile.mode' option can only be one of \"auto\", \"true\", \"false\", or \"user\". ") + if (!(mode %in% c("auto", "true", "false"))) { + stop("'teal.lockfile.mode' option can only be one of \"auto\", \"true\" or \"false\". ") } if (mode == "false") { @@ -86,19 +86,6 @@ srv_teal_lockfile <- function(id) { return(NULL) } - if (mode == "user") { - user_lockfile_path <- getOption("teal.lockfile.path", default = "") - if (file.exists(user_lockfile_path)) { - file.copy(user_lockfile_path, lockfile_path) - logger::log_debug('Lockfile set using option "teal.lockfile.path" - skipping automatic creation.') - enable_lockfile_download() - } else { - warning("Lockfile provided through options('teal.lockfile.path') does not exist.", call. = FALSE) - shinyjs::hide("lockFileLink") - } - return(NULL) - } - if (file.exists(lockfile_path)) { logger::log_debug("Lockfile has already been created for this app - skipping automatic creation.") enable_lockfile_download() diff --git a/tests/testthat/test-module_teal.R b/tests/testthat/test-module_teal.R index c9853c26b9..8777a73aca 100644 --- a/tests/testthat/test-module_teal.R +++ b/tests/testthat/test-module_teal.R @@ -66,7 +66,10 @@ transform_list <<- list( ) testthat::describe("srv_teal lockfile", { - testthat::it("creation process is invoked and snapshot is copied to teal_app.lock and removed after session ended", { + testthat::it(paste0( + "creation process is invoked for teal.lockfile.mode = \"true\" ", + "and snapshot is copied to teal_app.lock and removed after session ended" + ), { withr::with_options( list(teal.lockfile.mode = "true"), { @@ -91,14 +94,9 @@ testthat::describe("srv_teal lockfile", { } ) }) - testthat::it("is copied from option-teal.lockfile.path and then removed from an app directory", { - temp_lockfile_mock <- tempfile() - writeLines("test", temp_lockfile_mock) + testthat::it("creation process is not invoked for teal.lockfile.mode = \"false\"", { withr::with_options( - list( - teal.lockfile.mode = "user", - teal.lockfile.path = temp_lockfile_mock - ), + list(teal.lockfile.mode = "false"), { renv_filename <- "teal_app.lock" shiny::testServer( @@ -109,12 +107,11 @@ testthat::describe("srv_teal lockfile", { modules = modules(example_module()) ), expr = { - testthat::expect_true(file.exists(renv_filename)) + testthat::expect_false(file.exists(renv_filename)) } ) } ) - testthat::expect_false(file.exists(renv_filename)) }) }) diff --git a/vignettes/teal-options.Rmd b/vignettes/teal-options.Rmd index 60d7e5d7f6..2d28b818af 100644 --- a/vignettes/teal-options.Rmd +++ b/vignettes/teal-options.Rmd @@ -93,20 +93,11 @@ Values: * `"auto"` - auto detect whether to compute `lockfile` * `"true"` - compute `lockfile` and show `"download lockfile"` in the footer * `"false"` - do not compute `lockfile` and do not show `"download lockfile"` in the footer -* `"user"` - use pre-computed `lockfile` specified in `teal.lockfile.path` and show `"download lockfile"` in the footer - Default: `"auto"`. To read more about lockfile usage creation check `?teal::module_teal_lockfile`. -### `teal.lockfile.path` (`character`) - -The path to the pre-computed `renv` lockfile linked by app developer. -Used when `teal.lockfile.enable` is set to `"user"`. -If files does not exist, the warning is thrown and `"download lockfile"` in the footer is not shown. - -To read more about lockfile usage creation check `?teal::module_teal_lockfile`. # Deprecated options From 13c62a090679cd9741682a084bae887f27652be3 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 20 Sep 2024 09:08:30 +0000 Subject: [PATCH 82/85] [skip style] [skip vbump] Restyle files --- tests/testthat/test-module_teal.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/testthat/test-module_teal.R b/tests/testthat/test-module_teal.R index 8777a73aca..44751a29fc 100644 --- a/tests/testthat/test-module_teal.R +++ b/tests/testthat/test-module_teal.R @@ -69,7 +69,7 @@ testthat::describe("srv_teal lockfile", { testthat::it(paste0( "creation process is invoked for teal.lockfile.mode = \"true\" ", "and snapshot is copied to teal_app.lock and removed after session ended" - ), { + ), { withr::with_options( list(teal.lockfile.mode = "true"), { From 9653aa463f8258bbcba1a72b3fc505eaeefaeab8 Mon Sep 17 00:00:00 2001 From: m7pr Date: Fri, 20 Sep 2024 11:12:15 +0200 Subject: [PATCH 83/85] rename teal.lockfile.mode to "enabled" and "disabled" --- R/module_teal_lockfile.R | 8 ++++---- tests/testthat/test-module_teal.R | 8 ++++---- vignettes/teal-options.Rmd | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/R/module_teal_lockfile.R b/R/module_teal_lockfile.R index a3b60637eb..409e2a0ef6 100644 --- a/R/module_teal_lockfile.R +++ b/R/module_teal_lockfile.R @@ -76,12 +76,12 @@ srv_teal_lockfile <- function(id) { lockfile_path <- "teal_app.lock" mode <- getOption("teal.lockfile.mode", default = "") - if (!(mode %in% c("auto", "true", "false"))) { - stop("'teal.lockfile.mode' option can only be one of \"auto\", \"true\" or \"false\". ") + if (!(mode %in% c("auto", "enabled", "disabled"))) { + stop("'teal.lockfile.mode' option can only be one of \"auto\", \"disabled\" or \"disabled\". ") } - if (mode == "false") { - logger::log_debug("'teal.lockfile.mode' option is set to 'false'. Hiding lockfile download button.") + if (mode == "disabled") { + logger::log_debug("'teal.lockfile.mode' option is set to 'disabled'. Hiding lockfile download button.") shinyjs::hide("lockFileLink") return(NULL) } diff --git a/tests/testthat/test-module_teal.R b/tests/testthat/test-module_teal.R index 8777a73aca..4d0751fb4d 100644 --- a/tests/testthat/test-module_teal.R +++ b/tests/testthat/test-module_teal.R @@ -67,11 +67,11 @@ transform_list <<- list( testthat::describe("srv_teal lockfile", { testthat::it(paste0( - "creation process is invoked for teal.lockfile.mode = \"true\" ", + "creation process is invoked for teal.lockfile.mode = \"enabled\" ", "and snapshot is copied to teal_app.lock and removed after session ended" ), { withr::with_options( - list(teal.lockfile.mode = "true"), + list(teal.lockfile.mode = "enabled"), { renv_filename <- "teal_app.lock" shiny::testServer( @@ -94,9 +94,9 @@ testthat::describe("srv_teal lockfile", { } ) }) - testthat::it("creation process is not invoked for teal.lockfile.mode = \"false\"", { + testthat::it("creation process is not invoked for teal.lockfile.mode = \"disabled\"", { withr::with_options( - list(teal.lockfile.mode = "false"), + list(teal.lockfile.mode = "disabled"), { renv_filename <- "teal_app.lock" shiny::testServer( diff --git a/vignettes/teal-options.Rmd b/vignettes/teal-options.Rmd index 2d28b818af..716f163cd6 100644 --- a/vignettes/teal-options.Rmd +++ b/vignettes/teal-options.Rmd @@ -91,8 +91,8 @@ This enables app users to download `renv` lockfile by clicking `"download lockfi Values: * `"auto"` - auto detect whether to compute `lockfile` -* `"true"` - compute `lockfile` and show `"download lockfile"` in the footer -* `"false"` - do not compute `lockfile` and do not show `"download lockfile"` in the footer +* `"enabled"` - compute `lockfile` and show `"download lockfile"` in the footer +* `"disabled"` - do not compute `lockfile` and do not show `"download lockfile"` in the footer Default: `"auto"`. From ab771201fb82e1c0403bb07e0050576cab94d21d Mon Sep 17 00:00:00 2001 From: m7pr Date: Fri, 20 Sep 2024 12:18:59 +0200 Subject: [PATCH 84/85] remove section about user-specified lockfile from module_teal_lockfile documetnation --- R/module_teal_lockfile.R | 21 ++++++++------------- man/module_teal_lockfile.Rd | 13 ++----------- 2 files changed, 10 insertions(+), 24 deletions(-) diff --git a/R/module_teal_lockfile.R b/R/module_teal_lockfile.R index 409e2a0ef6..5dd058d1f0 100644 --- a/R/module_teal_lockfile.R +++ b/R/module_teal_lockfile.R @@ -5,19 +5,14 @@ #' @section Different ways of creating lockfile: #' `teal` leverages [renv::snapshot()], which offers multiple methods for lockfile creation. #' -#' - User-specified: -#' - **Pre-computed lockfile**: Users can provide their own pre-computed lockfile by specifying the path via -#' `teal.lockfile.path` option. Automatic lockfile computation is skipped in such case. -#' - Automatically computed: -#' - **Working directory lockfile**: If `teal.lockfile.path` is not set, `teal` will, by default, create an -#' `implicit` type lockfile that uses `renv::dependencies()` to detect all R packages in the current project's -#' working directory. -#' - **`DESCRIPTION`-based lockfile**: To generate a lockfile based on a `DESCRIPTION` file in your working -#' directory, set `renv::settings$snapshot.type("explicit")`. The naming convention for `type` follows -#' `renv::snapshot()`. For the `"explicit"` type, refer to `renv::settings$package.dependency.fields()` for the -#' `DESCRIPTION` fields included in the lockfile. -#' - **Custom files-based lockfile**: To specify custom files as the basis for the lockfile, set -#' `renv::settings$snapshot.type("custom")` and configure the `renv.snapshot.filter` option. +#' - **Working directory lockfile**: `teal`, by default, will create an `implicit` type lockfile that uses +#' `renv::dependencies()` to detect all R packages in the current project's working directory. +#' - **`DESCRIPTION`-based lockfile**: To generate a lockfile based on a `DESCRIPTION` file in your working +#' directory, set `renv::settings$snapshot.type("explicit")`. The naming convention for `type` follows +#' `renv::snapshot()`. For the `"explicit"` type, refer to `renv::settings$package.dependency.fields()` for the +#' `DESCRIPTION` fields included in the lockfile. +#' - **Custom files-based lockfile**: To specify custom files as the basis for the lockfile, set +#' `renv::settings$snapshot.type("custom")` and configure the `renv.snapshot.filter` option. #' #' @section lockfile usage: #' After creating the lockfile, you can restore the application's environment using `renv::restore()`. diff --git a/man/module_teal_lockfile.Rd b/man/module_teal_lockfile.Rd index 17e4aa357e..c4f7170dfd 100644 --- a/man/module_teal_lockfile.Rd +++ b/man/module_teal_lockfile.Rd @@ -35,16 +35,8 @@ Generate lockfile for application's environment reproducibility \code{teal} leverages \code{\link[renv:snapshot]{renv::snapshot()}}, which offers multiple methods for lockfile creation. \itemize{ -\item User-specified: -\itemize{ -\item \strong{Pre-computed lockfile}: Users can provide their own pre-computed lockfile by specifying the path via -\code{teal.lockfile.path} option. Automatic lockfile computation is skipped in such case. -} -\item Automatically computed: -\itemize{ -\item \strong{Working directory lockfile}: If \code{teal.lockfile.path} is not set, \code{teal} will, by default, create an -\code{implicit} type lockfile that uses \code{renv::dependencies()} to detect all R packages in the current project's -working directory. +\item \strong{Working directory lockfile}: \code{teal}, by default, will create an \code{implicit} type lockfile that uses +\code{renv::dependencies()} to detect all R packages in the current project's working directory. \item \strong{\code{DESCRIPTION}-based lockfile}: To generate a lockfile based on a \code{DESCRIPTION} file in your working directory, set \code{renv::settings$snapshot.type("explicit")}. The naming convention for \code{type} follows \code{renv::snapshot()}. For the \code{"explicit"} type, refer to \code{renv::settings$package.dependency.fields()} for the @@ -53,7 +45,6 @@ directory, set \code{renv::settings$snapshot.type("explicit")}. The naming conve \code{renv::settings$snapshot.type("custom")} and configure the \code{renv.snapshot.filter} option. } } -} \section{lockfile usage}{ From 3ffc3de1b4f774c28fdc6edc2f0488014e4eaa37 Mon Sep 17 00:00:00 2001 From: m7pr Date: Fri, 20 Sep 2024 15:00:19 +0200 Subject: [PATCH 85/85] rephrase vignette wording --- vignettes/teal-options.Rmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vignettes/teal-options.Rmd b/vignettes/teal-options.Rmd index 716f163cd6..3ab894456e 100644 --- a/vignettes/teal-options.Rmd +++ b/vignettes/teal-options.Rmd @@ -86,7 +86,7 @@ Default: `FALSE`. ### `teal.lockfile.mode` (`character`) -This enables app users to download `renv` lockfile by clicking `"download lockfile"` in the footer. +This enables to compute `renv` lockfile and shows a button to `"download lockfile"` in the footer. Values: