diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index 0df5e77..2e51132 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -2,9 +2,9 @@ # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help on: push: - branches: [main, master] + branches: [main, master, dev] pull_request: - branches: [main, master] + branches: [main, master, dev] name: R-CMD-check diff --git a/DESCRIPTION b/DESCRIPTION index 6c9362d..de0725c 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: checked Title: Systematically Run R CMD Checks -Version: 0.5.1.9000 +Version: 0.5.4 Authors@R: c( person( diff --git a/NAMESPACE b/NAMESPACE index 8362e7a..0affc8a 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -138,7 +138,6 @@ export(new_rev_dep_checker) export(pkg_origin) export(pkg_origin_archive) export(pkg_origin_base) -export(pkg_origin_is_base) export(pkg_origin_local) export(pkg_origin_remote) export(pkg_origin_repo) diff --git a/NEWS.md b/NEWS.md index 36bab28..7534ead 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,7 +1,45 @@ -# checked (development) +# checked 0.5.4 + +* Improve error messaging when using basic_tty + +* Various improvements to reporters and minor display tweaks + +# checked 0.5.3 + +* Make is possible to construct reporter environments with additional values + to better control their behavior. + +* Add `checks_only` parameter to `reporter_basic_tty` which if set to TRUE + will make the reporter broadcast only check tasks instead of both + install and check. * Address changes related to default parameters changes in `callr` and resulting NULL comparison in `checked` (@gaborcsardi, #93) + +* Multiple API changes facilitating additional customization for tasks. + +* Ensure packages destined into isolated libraries or those coming from + non-standard sources are always installed. + +* Redesign logs by grouping them into package specific directories. + +# checked 0.5.2 + +* Add timers striping to `strip_details_from_issue()` to avoid false-positives. + +* Remove `pkg_origin_is_base()` helper function and use memoised `base_pkgs()`. + +* Update `RE_CHECK` to capture even more edge cases while parsing R CMD check + output. + +* Finish check process even if checks seem incomplete but 3 or mire minutes have + passed since the process ended to avoid infinite loops. + +* Further improvements to the check process finisher. + +* Make `graph_dedup_attrs` rebuild the graph from scratch with deduplicated + attributes rather than manipulating the exiting graph. I significantly speeds + up the function. # checked 0.5.1 diff --git a/R/check_process.R b/R/check_process.R index 916306b..a36046b 100644 --- a/R/check_process.R +++ b/R/check_process.R @@ -1,7 +1,6 @@ # Regular Expression for Parsing R CMD check checks # nolint start, styler: off RE_CHECK <- paste0( - "(?<=^|\\n)", # must start at beginning of string OR right after a newline "\\* checking ", # literal "* checking " "(?.*?)", # capture check name/content (non-greedy) as "check" " \\.\\.\\.", # followed by literal " ..." @@ -11,23 +10,27 @@ RE_CHECK <- paste0( "\\n", # or: a normal detail line starting on the next line "(?:[ \\t]{2,}", # either indented (2+ spaces/tabs) "|\\*(?! (?:DONE|checking )))", # or a '*' line that is NOT "* DONE" and NOT "* checking ..." - "[^\\n]*(?:\\n|$)", # consume the rest of that detail line (to newline/end) + "[^\\n]*?", # consume content... + "(?:\\n|$|(?=\\* (?:DONE|checking )))", # ...until newline/end OR before next "* DONE"/"* checking" ")*", "[ \\t]*", # allow extra spaces/tabs after "..." on the SAME line "(?:", # position the engine right before a status token if one exists # Case 1: status token is on the current line (possibly preceded by comment text) - "(?:[^\\n]*[ \\t]+)?(?=(?:[A-Z]{2}[A-Z0-9_-]*)\\s*(?:\\n|$))", + "(?:(?:(?!\\* (?:DONE|checking ))[^\\n])*[ \\t]+)?", + "(?=(?:[A-Z]{2}[A-Z0-9_-]*)\\s*(?:\\n|$|\\* (?:DONE|checking )))", "|", # Case 2: status token is on the next line: # consume remainder of current line + newline + optional indent, - # but only if the next thing is a status token at end-of-line - "[^\\n]*\\n[ \\t]*(?=(?:[A-Z]{2}[A-Z0-9_-]*)\\s*(?:\\n|$))", + # but only if the next thing is a status token at end-of-line (or right before next marker) + "(?:(?!\\* (?:DONE|checking ))[^\\n])*\\n[ \\t]*", + "(?=(?:[A-Z]{2}[A-Z0-9_-]*)\\s*(?:\\n|$|\\* (?:DONE|checking )))", "|", - # Case 3: no status token (eat remainder/comment and stop here) - "[^\\n]*", + # Case 3: no status token (eat remainder/comment and stop here), + # but do NOT swallow the next "* checking/* DONE" if it's glued to this line + "[^\\n]*?(?=\\n|$|\\* (?:DONE|checking ))", ")", - "(?(?:[A-Z]{2}[A-Z0-9_-]*)|)", # capture status token, or capture empty string if absent - "(?=\\s*(?:\\n|$))" # must end at newline/end (allow trailing whitespace) + "(?(?:[A-Z]{2}[A-Z0-9_-]*)|)", # status token OR empty string + "(?=\\s*(?:\\n|$|\\* (?:DONE|checking )))" # terminated by newline/end OR next marker ) # nolint end, styler: on @@ -68,14 +71,18 @@ check_process <- R6::R6Class( if (!self$is_alive()) callback(self) }, finish = function() { - # self$checks active binding calls poll_output so there is not need - # to call it explicitly + # Make sure results are polled + self$poll_output() checks <- self$checks # In some cases, check subprocess might suffer from a race condition, when # process itself finished, but the final results of the last subcheck # are not yet available to parse. Therefore we allow the process to - # finalize only if the last subcheck has reported status. - if (checks[length(checks)] != "") { + # finalize only if the last subcheck has reported status. However + # if we stuck in this state for longer than 3 minutes we should + # try to finish anyway, to prevent possible infinite loops. + time_finished <- self$get_time_finish() %||% Sys.time() + if (checks[length(checks)] != "" || + ((Sys.time() - time_finished) >= as.difftime(3, units = "mins"))) { self$save_results() private$cache_parsed_results() private$free_file_descriptors() diff --git a/R/checker.R b/R/checker.R index 16adfb5..272fb16 100644 --- a/R/checker.R +++ b/R/checker.R @@ -82,6 +82,11 @@ checker <- R6::R6Class( #' @param restore `logical` value, whether output directory should be #' unlinked before running checks. If `FALSE`, an attempt will me made to #' restore previous progress from the same `output`. + #' @param dependencies A vector of length one or a named list. + #' Compatible with [`as_pkg_dependencies`]. + #' @param upgrade `logical` value, whether packages should be upgraded + #' if more recent version is discovered in available sources. Remotes + #' packages, if allowed to be used, are always installed and prioritized. #' @param ... Additional arguments unused #' #' @return [checker]. @@ -95,6 +100,8 @@ checker <- R6::R6Class( lib.loc = .libPaths(), repos = getOption("repos"), restore = options::opt("restore"), + dependencies = TRUE, + upgrade = FALSE, ... ) { check_past_output(output, restore, ask = interactive()) @@ -110,8 +117,9 @@ checker <- R6::R6Class( lib.loc ) private$repos <- repos + private$upgrade <- upgrade - self$graph <- task_graph(self$plan, repos) + self$graph <- task_graph(self$plan, repos, dependencies = dependencies) private$restore_complete_checks() }, @@ -183,7 +191,8 @@ checker <- R6::R6Class( node = next_node, g = self$graph, output = self$output, - lib.loc = private$lib.loc + lib.loc = private$lib.loc, + upgrade = private$upgrade ) if (is.null(process)) { @@ -219,6 +228,14 @@ checker <- R6::R6Class( all(V(self$graph)$status == STATUS$done) } + }, + #' @description + #' Tasks + #' + #' Returns what type of tasks the checker consists of and returns a unique + #' vector of primary classes + tasks = function() { + unique(vcapply(V(self$graph)$task, function(x) class(x)[[1]])) } ), private = list( @@ -240,6 +257,9 @@ checker <- R6::R6Class( # task loop counter gc_needed = FALSE, + # upgrade flag + upgrade = FALSE, + start_node = function(node) { task_graph_package_status(self$graph, node) <- STATUS$`in progress` private$start_node_meta_parents(node) diff --git a/R/install.R b/R/install.R index af17f08..93adbae 100644 --- a/R/install.R +++ b/R/install.R @@ -12,38 +12,38 @@ install_process <- R6::R6Class( lib = .libPaths()[[1]], libpaths = .libPaths(), available_packages_filters = getOption("available_packages_filters"), - log = NULL + log = NULL, + env = callr::rcmd_safe_env() ) { if (!dir.exists(lib)) dir.create(lib, recursive = TRUE) private$package <- pkgs self$log <- log private$callr_r_bg( - function(..., escalate_warning, available_packages_filters) { - options(available_packages_filters = available_packages_filters) - withCallingHandlers( - utils::install.packages(...), + function(..., available_packages_filters) { + options( + timeout = 600, + available_packages_filters = available_packages_filters + ) + invisible(capture.output(withCallingHandlers( + utils::install.packages(..., quiet = FALSE, verbose = TRUE), warning = function(w) { - if (escalate_warning(w)) { - print(w$message) - stop(w$message) - } else { - print(w$message) - warning(w) - } + print(w$message) } - ) + ), split = TRUE)) }, args = list( private$package, ..., lib = lib, - escalate_warning = is_install_failure_warning, available_packages_filters = available_packages_filters ), libpath = libpaths, stdout = self$log, stderr = "2>&1", - system_profile = TRUE + system_profile = options::opt("install_system_profile"), + user_profile = options::opt("install_user_profile"), + cmdargs = c("--slave", "--no-save", "--no-restore", "--vanilla"), + env = env ) }, get_duration = function() { @@ -62,7 +62,26 @@ install_process <- R6::R6Class( if (is.function(f <- private$finish_callback)) f(self) }, get_r_exit_status = function() { - as.integer(inherits(try(self$get_result(), silent = TRUE), "try-error")) + res <- self$get_results_safe() + if (inherits(self$get_results_safe(), "callr_error")) { + 1L + } else if (any(is_install_failure_warning(res))) { + 1L + } else { + 0L + } + + }, + get_results_safe = function() { + tryCatch( + gsub("\\n", "\n", self$get_result(), fixed = TRUE), + error = function(e) { + e + }, + warning = function(w) { + w + } + ) } ), private = list( @@ -109,5 +128,5 @@ is_install_failure_warning <- function(w) { ) re <- paste0("(", paste0(patterns, collapse = "|"), ")") - grepl(re, w$message) + grepl(re, w) } diff --git a/R/lib.R b/R/lib.R index 5cdb860..15eabbf 100644 --- a/R/lib.R +++ b/R/lib.R @@ -50,6 +50,10 @@ format.lib_path <- function(x, ...) { "library" } +is_lib_path_default <- function(task) { + inherits(task$lib, "lib_path_default") +} + #' Get Library Location #' #' @param x An object describing a library location diff --git a/R/next_task.R b/R/next_task.R index b832ba1..8d9731a 100644 --- a/R/next_task.R +++ b/R/next_task.R @@ -62,36 +62,44 @@ start_task.install_task <- function( g, output, lib.loc, + upgrade, ... ) { task <- node$task[[1]] + libpaths <- unique(c( task_graph_libpaths(g, node, lib.loc = lib.loc, output = output), lib.loc )) install_parameters <- install_params(task$origin) - if (any(inherits(task$origin, c("pkg_origin_base", "pkg_origin_unknown")))) { - return(NULL) - } + is_base <- any( + inherits(task$origin, c("pkg_origin_base", "pkg_origin_unknown")) + ) + if (is_base) return(NULL) # install_parameters$package is a valid package name only for # pkg_origin_repo. Otherwise it's a path to the source package in which case # is_package_installed returns FALSE (as it should) - if (is_package_installed(install_parameters$package, libpaths)) { + is_installed <- is_package_installed( + install_parameters$package, + libpaths, + upgrade %nif% task$origin$version + ) + + if (is_installed && is_lib_path_default(task) && inherits(task$origin, "pkg_origin_repo")) # nolint return(NULL) - } install_process$new( install_parameters$package, lib = lib(task, lib.loc = lib.loc, lib.root = path_libs(output)), libpaths = libpaths, - repos = task$origin$repos, + repos = install_parameters$repos, dependencies = FALSE, type = task$type, - INSTALL_opts = c(), # TODO - log = path_install_log(output, node$name[[1]]), - env = c() # TODO + INSTALL_opts = task$INSTALL_opts, + log = path_install_log(output, package(task), node$name[[1]]), + env = task$env ) } diff --git a/R/options.R b/R/options.R index e5be46d..a81f764 100644 --- a/R/options.R +++ b/R/options.R @@ -67,7 +67,19 @@ options::define_options( envvar_fn = structure( function(raw, ...) trimws(strsplit(raw, " ")[[1]]), desc = "space-separated R CMD check flags" - ) + ), + + "named `character` vector of environment variables to use during + the package installation.", + install_envvars = callr::rcmd_safe_env(), + + "`logical` used as `sytem_profile` parameter passed to the `callr::r_bg()` + function used to install packages", + install_system_profile = FALSE, + + "value used as `user_profile` parameter passed to the `callr::r_bg()` + function used to install packages", + install_user_profile = "project" ) #' @eval options::as_roxygen_docs() diff --git a/R/pkg_origin.R b/R/pkg_origin.R index 40ebedd..6a8e458 100644 --- a/R/pkg_origin.R +++ b/R/pkg_origin.R @@ -57,16 +57,13 @@ pkg_origin_repo <- function(package, repos, ...) { ap_pkg <- available_packages(repos = repos)[package, ] version <- package_version(ap_pkg["Version"]) - source <- strip_src_contrib(ap_pkg["Repository"]) - if (any(which <- startsWith(repos, source))) { - source <- repos[which][1] - } + repo <- strip_src_contrib(ap_pkg["Repository"], repos = repos) pkg_origin( package = package, version = version, - source = source, - repos = repos, + source = repo, + repos = repo, ..., .class = "pkg_origin_repo" ) @@ -74,7 +71,7 @@ pkg_origin_repo <- function(package, repos, ...) { try_pkg_origin_repo <- function(package, repos, ...) { - if (isTRUE(pkg_origin_is_base(package))) { + if (package %in% base_pkgs()) { pkg_origin_base(package, ...) } else if (package %in% available_packages(repos = repos)[, "Package"]) { pkg_origin_repo(package = package, repos = repos, ...) @@ -84,16 +81,6 @@ try_pkg_origin_repo <- function(package, repos, ...) { } -#' @export -#' @rdname pkg_origin -pkg_origin_is_base <- function(package, ...) { - is_base <- package == "R" - is_inst <- package %in% utils::installed.packages()[, "Package"] - is_base[is_inst] <- utils::installed.packages()[package[is_inst], "Priority"] == "base" # nolint - is_base -} - - #' @export #' @rdname pkg_origin pkg_origin_base <- function(package, ...) { @@ -214,7 +201,7 @@ pkg_deps.pkg_origin_local <- function( indirect_deps <- pkg_dependencies( packages = direct_deps$name, - dependencies = dependencies, + dependencies = "hard", db = db ) indirect_deps$depth <- rep.int("indirect", NROW(indirect_deps)) diff --git a/R/plan.R b/R/plan.R index eda9c32..25fbeba 100644 --- a/R/plan.R +++ b/R/plan.R @@ -7,12 +7,16 @@ #' #' @param path path to the package source. #' @param repos repository used to identify reverse dependencies. +#' @param remotes_dependencies A vector of length one or a named list. +#' Compatible with [`as_pkg_dependencies`]. Used to filter out remotes +#' dependencies. #' #' @family plan #' @export plan_rev_dep_checks <- function( path, - repos = getOption("repos") + repos = getOption("repos"), + remotes_dependencies = TRUE ) { path <- check_path_is_pkg_source(path) ap <- available_packages(repos = repos) @@ -75,7 +79,7 @@ plan_rev_dep_checks <- function( g <- task_graph_class(g) if (remotes_permitted()) { - remotes_graph(g) + remotes_graph(g, dependencies = remotes_dependencies) } else { g } @@ -131,6 +135,9 @@ plan_rev_dep_release_check <- function(origin, revdep, repos) { #' @param package A path to either package, directory with packages or name #' of the package (details) #' @param repos repository used to identify packages when name is provided. +#' @param remotes_dependencies A vector of length one or a named list. +#' Compatible with [`as_pkg_dependencies`]. Used to filter out remotes +#' dependencies. #' #' @details #' `package` parameter has two different allowed values: @@ -145,7 +152,8 @@ plan_rev_dep_release_check <- function(origin, revdep, repos) { #' @export plan_local_checks <- function( package, - repos = getOption("repos") + repos = getOption("repos"), + remotes_dependencies = TRUE ) { task <- meta_task( @@ -179,7 +187,7 @@ plan_local_checks <- function( star_plan_template(c( list(task), local_checks_tasks - )) + ), remotes_dependencies) } @@ -189,11 +197,18 @@ plan_local_checks <- function( #' #' @param package A path to package source. #' @param repos repository used to identify packages when name is provided. +#' @param remotes_dependencies A vector of length one or a named list. +#' Compatible with [`as_pkg_dependencies`]. Used to filter out remotes +#' dependencies. +#' @param INSTALL_opts Options to set while the root package is being installed. +#' Check [`utils::install.packages`] for details. #' #' @family plan plan_local_install <- function( package, - repos = getOption("repos") + repos = getOption("repos"), + remotes_dependencies = TRUE, + INSTALL_opts = c() ) { m_task <- meta_task( @@ -202,16 +217,17 @@ plan_local_install <- function( ) i_task <- install_task( - origin = pkg_origin_local(package) + origin = pkg_origin_local(package), + INSTALL_opts = INSTALL_opts ) star_plan_template(list( m_task, i_task - )) + ), remotes_dependencies) } -star_plan_template <- function(tasks) { +star_plan_template <- function(tasks, remotes_dependencies) { g <- star_graph( task = tasks ) @@ -220,8 +236,8 @@ star_plan_template <- function(tasks) { g <- task_graph_class(g) - if (remotes_permitted()) { - remotes_graph(g) + if (remotes_permitted() && !isFALSE(remotes_dependencies)) { + remotes_graph(g, dependencies = remotes_dependencies) } else { g } diff --git a/R/remotes.R b/R/remotes.R index 198dafc..23e5c6f 100644 --- a/R/remotes.R +++ b/R/remotes.R @@ -3,9 +3,14 @@ remotes_graph <- function(x, ...) { } #' @export -remotes_graph.task_graph <- function(x, ...) { +remotes_graph.task_graph <- function(x, ..., dependencies = TRUE) { vs <- V(x) - remotes_subgraphs <- lapply(vs, remotes_graph, vs = vs) + remotes_subgraphs <- lapply( + vs, + remotes_graph, + vs = vs, + dependencies = dependencies + ) task_graph_class( suppressWarningsRegex( @@ -23,13 +28,13 @@ remotes_graph.task_graph <- function(x, ...) { #' @export remotes_graph.integer <- function(x, ..., vs) { - remotes_graph(vs[[x]]) + remotes_graph(vs[[x]], ...) } #' @export #' @method remotes_graph igraph.vs remotes_graph.igraph.vs <- function(x, ...) { - remotes_graph(x$task) + remotes_graph(x$task, ...) } #' @export @@ -40,15 +45,23 @@ remotes_graph.task <- function(x, ...) { } #' @export -remotes_graph.install_task <- function(x, ...) { +remotes_graph.install_task <- function(x, ..., dependencies = TRUE) { remote_tasks <- get_remote_tasks(x) if (length(remote_tasks) == 0) return(igraph::make_empty_graph()) remote_tasks_names <- vcapply(remote_tasks, package) + dependencies <- as_pkg_dependencies(dependencies)$direct x_deps <- pkg_deps(x$origin) - x_remote_deps <- x_deps[ - x_deps$package == package(x) & x_deps$name %in% remote_tasks_names, - ] + x_remote_deps <- x_deps[x_deps$package == package(x) & + x_deps$name %in% remote_tasks_names & + x_deps$type %in% dependencies, ] + + if (NROW(x_remote_deps) == 0) return(igraph::make_empty_graph()) + + # Filter out deps out of scopr + remote_tasks <- remote_tasks[remote_tasks_names %in% x_remote_deps$name] + remote_tasks_names <- + remote_tasks_names[remote_tasks_names %in% x_remote_deps$name] # Sort tasks according to same key remote_tasks <- remote_tasks[order(remote_tasks_names)] diff --git a/R/reporter.R b/R/reporter.R index ace72b6..3b5c43b 100644 --- a/R/reporter.R +++ b/R/reporter.R @@ -21,37 +21,49 @@ #' A line-feed reporter presenting output one line at a time, providing #' a reporter with minimal assumptions about terminal capabilities. #' +#' @param checks_only whether basic tty reporter should report only check tasks. +#' @param checker checker object required to properly derive default reporter. +#' @param ... additional values which should be assigned to the reported +#' environment. +#' #' @family reporters #' @name reporters NULL -reporter <- function(type) { +reporter <- function(type, ...) { type <- paste0("reporter_", type) - structure(new.env(parent = baseenv()), class = c(type, "reporter")) + reporter_options <- list(...) + structure( + list2env(reporter_options, parent = baseenv(), hash = TRUE), + class = c(type, "reporter") + ) } #' @rdname reporters #' @export -reporter_ansi_tty <- function() { - reporter("ansi_tty") +reporter_ansi_tty <- function(...) { + reporter("ansi_tty", ...) } #' @rdname reporters #' @export -reporter_ansi_tty2 <- function() { - reporter("ansi_tty2") +reporter_ansi_tty2 <- function(...) { + reporter("ansi_tty2", ...) } #' @rdname reporters #' @export -reporter_basic_tty <- function() { - reporter("basic_tty") +reporter_basic_tty <- function(checks_only = FALSE, ...) { + reporter("basic_tty", checks_only = checks_only, ...) } #' @rdname reporters #' @export -reporter_default <- function() { - if (cli::is_ansi_tty()) { +reporter_default <- function(checker = NULL) { + is_revdep <- + !is.null(checker) && "rev_dep_check_meta_task" %in% checker$tasks() + + if (cli::is_ansi_tty() && is_revdep) { reporter_ansi_tty() } else if (cli::is_dynamic_tty()) { reporter_basic_tty() diff --git a/R/reporter_basic_tty.R b/R/reporter_basic_tty.R index 946d56e..1880570 100644 --- a/R/reporter_basic_tty.R +++ b/R/reporter_basic_tty.R @@ -7,15 +7,25 @@ report_start_setup.reporter_basic_tty <- function( ) { # start with all tasks initialized as pending v <- igraph::V(checker$graph) - v_actionable <- v[is_actionable_task(v$task)] + v_reportable <- if (reporter$checks_only) { + v[is_check(v$task)] + } else { + v[is_actionable_task(v$task)] + } # named factor vector, names as task aliases and value of last reported status - reporter$status <- rep(STATUS$pending, times = length(v_actionable)) - names(reporter$status) <- v_actionable$name + reporter$status <- rep(STATUS$pending, times = length(v_reportable)) + names(reporter$status) <- v_reportable$name reporter$time_start <- Sys.time() - cli::cli_text("<", utils::packageName(), "> Checks") + type <- if (all(vlapply(v_reportable$task, inherits, "install_task"))) { + "> Installs" + } else { + "> Checks" + } + + cli::cli_text("<", utils::packageName(), type) } #' @export @@ -37,6 +47,7 @@ report_status.reporter_basic_tty <- function(reporter, checker, envir) { p <- node$process + failure_message <- NULL # report stating of new checks if (!identical(node$status, reporter$status[[node$name]])) { status <- switch( # nolint @@ -44,16 +55,32 @@ report_status.reporter_basic_tty <- function(reporter, checker, envir) { "pending" = "queued", "in progress" = cli::cli_fmt(cli::cli_text("started")), "done" = { + pkgs_done <- reporter$status == STATUS$done # nolint + # +1 to acount for the task that is currently processed + fmt_count <- + cli::col_grey(" [{sum(pkgs_done)+1}/{length(pkgs_done)}]") + if (is.null(p)) { - cli::cli_fmt(cli::cli_text("finished (restored)")) + cli::cli_fmt( + cli::cli_text( + "finished", cli::format_inline(fmt_count), " (restored)" + ) + ) } else if (p$get_r_exit_status() != 0) { # checks processes don't have logs associated with it - message <- if (!is.null(p$log)) { - sprintf("failed (log: '%s')", p$log) - } else { - "failed" + failure_message <- if (inherits(p, "install_process")) { + paste( + p$get_results_safe(), + if (file.exists(p$log)) sprintf("\nfull log: '%s'", p$log), + sep = "\n", + collapse = "\n" + ) } - cli::cli_fmt(cli::cli_text(message)) + cli::cli_fmt( + cli::cli_text( + "failed", cli::format_inline(fmt_count) + ) + ) } else { dur <- if (!is.null(p$get_duration)) { p$get_duration() @@ -70,6 +97,7 @@ report_status.reporter_basic_tty <- function(reporter, checker, envir) { fmt_warning <- "{.warn {ewn[[2]]} WARNING{?/S}}" fmt_note <- "{.note {ewn[[3]]} NOTE{?/S}}" fmt_duration <- " {.time_taken ({format_time(dur)})}" + cli::cli_fmt(cli::cli_text( "finished", if (sum(ewn) > 0) " with ", @@ -78,6 +106,7 @@ report_status.reporter_basic_tty <- function(reporter, checker, envir) { if (ewn[[2]] > 0) cli::format_inline(fmt_warning), if (ewn[[3]] > 0) cli::format_inline(fmt_note) )), + cli::format_inline(fmt_count), if (!is.null(dur)) cli::format_inline(fmt_duration) )) } @@ -88,6 +117,7 @@ report_status.reporter_basic_tty <- function(reporter, checker, envir) { type <- format_task_type(node$task) # nolint (used via glue) prefix <- cli::col_cyan("[{format_time(time)}][{type}] ") cli::cli_text(prefix, "{.pkg {package(node$task)}} {status}") + if (!is.null(failure_message)) cli::cli_text(failure_message) reporter$status[[node$name]] <- node$status } } diff --git a/R/results.R b/R/results.R index dc7fd3a..7f22bab 100644 --- a/R/results.R +++ b/R/results.R @@ -427,6 +427,11 @@ strip_details_from_issue <- function(x) { pattern = "See(.*?)for details", replacement = "See for details" ) + x <- gsub( + x = x, + pattern = "\\[([^]]*)\\]", + replacement = "[]", + ) gsub( x = x, pattern = "[[:space:]]", diff --git a/R/run.R b/R/run.R index 01005b5..2b12df6 100644 --- a/R/run.R +++ b/R/run.R @@ -13,12 +13,12 @@ #' most expressive command-line reporter given your terminal capabilities. #' #' @export -run <- function(checker, ..., reporter = reporter_default()) { +run <- function(checker, ..., reporter = reporter_default(checker)) { UseMethod("run") } #' @export -run.character <- function(checker, ..., reporter = reporter_default()) { +run.character <- function(checker, ..., reporter = reporter_default(checker)) { checker <- new_rev_dep_checker(checker, ...) report_start_setup( reporter, @@ -30,7 +30,7 @@ run.character <- function(checker, ..., reporter = reporter_default()) { } #' @export -run.checker <- function(checker, ..., reporter = reporter_default()) { +run.checker <- function(checker, ..., reporter = reporter_default(checker)) { on.exit(add = TRUE, { checker$terminate() report_finalize(reporter, checker) diff --git a/R/task.R b/R/task.R index 940b480..5186b2a 100644 --- a/R/task.R +++ b/R/task.R @@ -53,6 +53,7 @@ print.task <- function(x, ...) { #' @param lib Any object that can be passed to [lib()] to generate a library #' path. #' @inheritParams utils::install.packages +#' @inheritParams callr::r_bg #' #' @family tasks #' @export @@ -61,6 +62,7 @@ install_task <- function( type = package_install_type(origin), INSTALL_opts = NULL, lib = lib_path(origin), + env = options::opt("install_envvars"), ... ) { task( @@ -68,6 +70,7 @@ install_task <- function( type = type, INSTALL_opts = INSTALL_opts, lib = lib, + env = env, ..., .subclass = "install" ) diff --git a/R/task_graph.R b/R/task_graph.R index 8f79499..6861cf9 100644 --- a/R/task_graph.R +++ b/R/task_graph.R @@ -14,6 +14,8 @@ #' @param x a `plan` object, containing a list of related steps. #' @param repos `repos`, as expected by [`tools::package_dependencies()`] to #' determine package relationships. +#' @param dependencies A vector of length one or a named list. Compatible with +#' [`as_pkg_dependencies`]. #' @param ... params passed to helper methods. #' @return A `data.frame` that can be used to build #' [`igraph::make_graph`] edges. @@ -26,18 +28,27 @@ #' @keywords internal #' #' @importFrom igraph V E -task_graph <- function(x, repos = getOption("repos"), ...) { +task_graph <- function( + x, repos = getOption("repos"), dependencies = TRUE, ... +) { UseMethod("task_graph") } #' @export -task_graph.task <- function(x, repos = getOption("repos"), ...) { - df <- pkg_deps(x$origin, repos = repos, dependencies = TRUE) +task_graph.task <- function( + x, + repos = getOption("repos"), + dependencies = TRUE, + ... +) { + df <- pkg_deps(x$origin, repos = repos, dependencies = dependencies) # Distinguish direct dependencies of the package form possible indirect # in the same data.frame which could come from suggested loops. This ensures # there is a separate node for the root task. - df$package[df$depth == "direct"] <- - paste(df$package[df$depth == "direct"], "root", sep = "-") + if (inherits(x, "check_task")) { + df$package[df$depth == "direct"] <- + paste(df$package[df$depth == "direct"], "root", sep = "-") + } colmap <- c("package" = "from", "name" = "to") rename <- match(names(df), names(colmap)) to_rename <- !is.na(rename) @@ -47,17 +58,14 @@ task_graph.task <- function(x, repos = getOption("repos"), ...) { E(g_dep)$relation <- RELATION$dep E(g_dep)$type <- DEP[E(g_dep)$type] - V(g_dep)$task <- lapply( - V(g_dep)$name, - function(p) { - if (endsWith(p, "-root")) { - x - } else { - origin <- try_pkg_origin_repo(package = p, repos = repos) - install_task(origin = origin) - } + V(g_dep)$task <- lapply(V(g_dep)$name, function(p) { + if (endsWith(p, "-root") || (inherits(x, "install_task") && p == package(x))) { # nolint + x + } else { + origin <- try_pkg_origin_repo(package = p, repos = repos) + install_task(origin = origin) } - ) + }) V(g_dep)$name <- vcapply(V(g_dep)$task, as_vertex_name) @@ -65,7 +73,9 @@ task_graph.task <- function(x, repos = getOption("repos"), ...) { } #' @export -task_graph.task_graph <- function(x, repos = getOption("repos"), ...) { +task_graph.task_graph <- function( + x, repos = getOption("repos"), dependencies = TRUE, ... +) { # only use dependency edges when populating graph nodes <- V(x)[is_actionable_task(V(x)$task)] @@ -82,7 +92,7 @@ task_graph.task_graph <- function(x, repos = getOption("repos"), ...) { subtree <- igraph::induced_subgraph(x, nh) # build dependency graph, with fallback installation task - deps <- task_graph(nh[[1]]$task, repos = repos) + deps <- task_graph(nh[[1]]$task, repos = repos, dependencies = dependencies) # merge trees on package names # NOTE: attributes (tasks) are preserved in the order they appear @@ -94,8 +104,12 @@ task_graph.task_graph <- function(x, repos = getOption("repos"), ...) { deduplicate_task_graph(subtree) }) + # then merge all the full check task task trees into a single graph g <- graph_dedup_attrs(igraph::union(x, check_task_neighborhoods)) + # Make sure orphaned packages, so those that do not lead to any meta tasks, + # are skipped + g <- task_graph_removed_orphaned(g) E(g)$type <- DEP[E(g)$type] V(g)$status <- STATUS$pending @@ -138,8 +152,16 @@ deduplicate_task_graph <- function(g) { ) } } - isolated <- which(igraph::degree(g) == 0) - igraph::delete_vertices(g, isolated) + g +} + +task_graph_removed_orphaned <- function(g) { + # The only package without "in" vertex should be meta tasks + vs <- igraph::V(g) + + isolated <- igraph::degree(g, mode = "in") == 0 & !is_meta(vs$task) + + igraph::delete_vertices(g, vs[isolated]) } dep_edges <- function(edges, dependencies = TRUE) { diff --git a/R/utils-deps.R b/R/utils-deps.R index 3122dcd..8f5399c 100644 --- a/R/utils-deps.R +++ b/R/utils-deps.R @@ -19,22 +19,21 @@ as_pkg_dependencies <- function(x) { x <- "hard" } else if (identical(x, TRUE)) { x <- "most" - } else if (identical(x, FALSE)) { + } else if (identical(x, FALSE) || identical(x, "self")) { x <- character(0L) } - deptypes <- c("Depends", "Imports", "LinkingTo", "Suggests", "Enhances") - if (is.character(x)) { + if ((is.character(x) || is.factor(x)) && length(x) > 0) { x <- switch(x, - "all" = deptypes, - "most" = c("Depends", "Imports", "LinkingTo", "Suggests"), - "hard" = c("Depends", "Imports", "LinkingTo"), - "soft" = c("Suggests", "Enhances"), - x + "all" = DEP, + "most" = DEP[c("Depends", "Imports", "LinkingTo", "Suggests")], + "hard" = DEP[c("Depends", "Imports", "LinkingTo")], + "soft" = DEP[c("Suggests", "Enhances")], + DEP[x] ) } - stopifnot(all(x %in% deptypes)) + stopifnot(all(x %in% DEP)) x } diff --git a/R/utils-igraph.R b/R/utils-igraph.R index 3b74be3..22f8070 100644 --- a/R/utils-igraph.R +++ b/R/utils-igraph.R @@ -44,48 +44,75 @@ star_graph <- function(...) { #' Primarily intended for cleaning up the result of an [`igraph::union()`], #' which adds duplicated attributes when attributes of the same name exist in #' multiple graphs. Searches for suffixes and consolidates attributes, -#' taking the attribute from the first non-NA value observed. +#' taking the attribute from the first non-NA value observed. The function +#' rebuilds the graph from scratch as accessing attributes once, operating on +#' lists and then assigning them to a new graph is significantly faster than +#' manipulating attribiutes of the existing graph. #' #' @param g task_graph object #' #' @keywords internal graph_dedup_attrs <- function(g) { - # pattern appended to duplicated attributes re <- "_\\d+$" + v_all <- igraph::vertex_attr(g) + e_all <- igraph::edge_attr(g) + v_attr_names <- names(v_all) + e_attr_names <- names(e_all) + v_dup <- grep(re, v_attr_names, value = TRUE) + e_dup <- grep(re, e_attr_names, value = TRUE) - # de-duplicate vertex attributes - v_attrs <- igraph::vertex_attr_names(g) - v_dup_attrs <- grep(re, v_attrs, value = TRUE) - v_dup_group <- sub(re, "", v_dup_attrs) - v_dup_attrs <- split(v_dup_attrs, v_dup_group) - for (i in seq_along(v_dup_attrs)) { - attr_name <- names(v_dup_attrs[i]) - attr_value <- igraph::vertex_attr(g, v_dup_attrs[[i]][[1L]]) - g <- igraph::delete_vertex_attr(g, v_dup_attrs[[i]][[1L]]) - for (attr_dup_name in v_dup_attrs[[i]][-1L]) { - is_na <- is.na(attr_value) - attr_value[is_na] <- igraph::vertex_attr(g, attr_dup_name)[is_na] - g <- igraph::delete_vertex_attr(g, attr_dup_name) + # Nothing to deduplicate + if (length(v_dup) == 0 && length(e_dup) == 0) return(g) + + first_non_na <- function(list) { + out <- list[[1]] + if (!any(is.na(out))) return(out) + + for (i in seq(2, length(list))) { + is_na <- is.na(out) + if (any(is_na)) out[is_na] <- list[[i]][is_na] } - g <- igraph::set_vertex_attr(g, attr_name, value = attr_value) + out } - # de-duplicate edge attributes - e_attrs <- igraph::edge_attr_names(g) - e_dup_attrs <- grep(re, e_attrs, value = TRUE) - e_dup_group <- sub(re, "", e_dup_attrs) - e_dup_attrs <- split(e_dup_attrs, e_dup_group) - for (i in seq_along(e_dup_attrs)) { - attr_name <- names(e_dup_attrs[i]) - attr_value <- igraph::edge_attr(g, e_dup_attrs[[i]][[1L]]) - g <- igraph::delete_edge_attr(g, e_dup_attrs[[i]][[1L]]) - for (attr_dup_name in e_dup_attrs[[i]][-1L]) { - is_na <- is.na(attr_value) - attr_value[is_na] <- igraph::edge_attr(g, attr_dup_name)[is_na] - g <- igraph::delete_edge_attr(g, attr_dup_name) - } - g <- igraph::set_edge_attr(g, attr_name, value = attr_value) + groups <- split(v_dup, sub(re, "", v_dup)) + for (base in names(groups)) { + cols <- groups[[base]] + v_all[[base]] <- first_non_na(v_all[cols]) + } + v_all[v_dup] <- NULL + + groups <- split(e_dup, sub(re, "", e_dup)) + for (base in names(groups)) { + cols <- groups[[base]] + e_all[[base]] <- first_non_na(e_all[cols]) + } + e_all[e_dup] <- NULL + + vertices <- data.frame(name = V(g)$name) + + for (attr in setdiff(names(v_all), "name")) { + vertices[[attr]] <- v_all[[attr]] } - g + # Build edges data.frame (keeps current edge order) + el <- igraph::as_edgelist(g, names = TRUE) + edges <- data.frame( + from = el[, 1], + to = el[, 2] + ) + + for (attr in names(e_all)) { + edges[[attr]] <- e_all[[attr]] + } + + # Rebuild graph + g_rebuilt <- igraph::graph_from_data_frame( + d = edges, + directed = TRUE, + vertices = vertices + ) + + class(g_rebuilt) <- class(g) + g_rebuilt } diff --git a/R/utils-paths.R b/R/utils-paths.R index 1ff40a1..ba14d53 100644 --- a/R/utils-paths.R +++ b/R/utils-paths.R @@ -67,9 +67,9 @@ path_checker_lib <- function(path) { normalizePath(p) } -path_install_log <- function(path, package, name = "lib") { - dir_create(p <- file.path(path_logs(path), name)) - normalizePath(file.path(p, sprintf("%s.log", package)), mustWork = FALSE) +path_install_log <- function(path, package, task) { + dir_create(p <- file.path(path_logs(path), package)) + normalizePath(file.path(p, sprintf("%s.log", task)), mustWork = FALSE) } path_logs <- function(path) { diff --git a/R/utils-pkg-source.R b/R/utils-pkg-source.R index 48719d5..67f8e80 100644 --- a/R/utils-pkg-source.R +++ b/R/utils-pkg-source.R @@ -1,5 +1,8 @@ -strip_src_contrib <- function(x) { - sub("/src/contrib$", "", x) +strip_src_contrib <- function(x, repos) { + match <- vlapply(repos, function(r) { + utils::contrib.url(r) == x + }) + repos[match] } check_dependencies <- function(dependencies) { diff --git a/R/utils.R b/R/utils.R index 9f45281..20b82ce 100644 --- a/R/utils.R +++ b/R/utils.R @@ -34,13 +34,28 @@ hashes <- function(x, ...) { vcapply(x, hash, ...) } -base_pkgs <- function() { +base_pkgs <- memoise::memoise(function() { c("R", utils::installed.packages(priority = "base")[, "Package"]) +}) + +is_package_installed <- function(pkg, lib.loc = .libPaths(), version = NULL) { + if (is.null(version)) { + path <- find.package(pkg, lib.loc = lib.loc, quiet = TRUE) + length(path) > 0 + } else { + tryCatch( + utils::packageVersion(pkg, lib.loc) >= version, + error = function(e) FALSE + ) + } } -is_package_installed <- function(pkg, lib.loc = .libPaths()) { - path <- find.package(pkg, lib.loc = lib.loc, quiet = TRUE) - length(path) > 0 +`%nif%` <- function(lhs, rhs) { + if (isTRUE(lhs)) { + rhs + } else { + NULL + } } .callr <- as.list(getNamespace("callr"), all.names = TRUE)[c( diff --git a/man/checker.Rd b/man/checker.Rd index f5cbff3..10116f2 100644 --- a/man/checker.Rd +++ b/man/checker.Rd @@ -62,6 +62,7 @@ be created and stored.} \item \href{#method-checker-step}{\code{checker$step()}} \item \href{#method-checker-start_next_task}{\code{checker$start_next_task()}} \item \href{#method-checker-is_done}{\code{checker$is_done()}} +\item \href{#method-checker-tasks}{\code{checker$tasks()}} \item \href{#method-checker-clone}{\code{checker$clone()}} } } @@ -81,6 +82,8 @@ and installation order are embedded. lib.loc = .libPaths(), repos = getOption("repos"), restore = options::opt("restore"), + dependencies = TRUE, + upgrade = FALSE, ... )}\if{html}{\out{}} } @@ -106,6 +109,13 @@ generating task graph and later pulling dependencies.} unlinked before running checks. If \code{FALSE}, an attempt will me made to restore previous progress from the same \code{output}.} +\item{\code{dependencies}}{A vector of length one or a named list. +Compatible with \code{\link{as_pkg_dependencies}}.} + +\item{\code{upgrade}}{\code{logical} value, whether packages should be upgraded +if more recent version is discovered in available sources. Remotes +packages, if allowed to be used, are always installed and prioritized.} + \item{\code{...}}{Additional arguments unused} } \if{html}{\out{}} @@ -185,6 +195,19 @@ Checks whether all the scheduled tasks were successfully executed. \if{html}{\out{
}}\preformatted{checker$is_done()}\if{html}{\out{
}} } +} +\if{html}{\out{
}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-checker-tasks}{}}} +\subsection{Method \code{tasks()}}{ +Tasks + +Returns what type of tasks the checker consists of and returns a unique +vector of primary classes +\subsection{Usage}{ +\if{html}{\out{
}}\preformatted{checker$tasks()}\if{html}{\out{
}} +} + } \if{html}{\out{
}} \if{html}{\out{}} diff --git a/man/graph_dedup_attrs.Rd b/man/graph_dedup_attrs.Rd index 5c3112e..0d62326 100644 --- a/man/graph_dedup_attrs.Rd +++ b/man/graph_dedup_attrs.Rd @@ -13,6 +13,9 @@ graph_dedup_attrs(g) Primarily intended for cleaning up the result of an \code{\link[igraph:union]{igraph::union()}}, which adds duplicated attributes when attributes of the same name exist in multiple graphs. Searches for suffixes and consolidates attributes, -taking the attribute from the first non-NA value observed. +taking the attribute from the first non-NA value observed. The function +rebuilds the graph from scratch as accessing attributes once, operating on +lists and then assigning them to a new graph is significantly faster than +manipulating attribiutes of the existing graph. } \keyword{internal} diff --git a/man/install_task.Rd b/man/install_task.Rd index 0663971..385aa3e 100644 --- a/man/install_task.Rd +++ b/man/install_task.Rd @@ -9,6 +9,7 @@ install_task( type = package_install_type(origin), INSTALL_opts = NULL, lib = lib_path(origin), + env = options::opt("install_envvars"), ... ) } @@ -33,6 +34,8 @@ install_task( \item{lib}{Any object that can be passed to \code{\link[=lib]{lib()}} to generate a library path.} +\item{env}{Environment variables to set for the child process.} + \item{...}{ further arguments to be passed to \code{\link[utils]{download.file}}, \code{\link[utils]{available.packages}}, or to the functions for binary diff --git a/man/options.Rd b/man/options.Rd index c00ce40..71c9de2 100644 --- a/man/options.Rd +++ b/man/options.Rd @@ -99,6 +99,27 @@ the R CMD check.\item{default: }{\preformatted{c(`_R_CHECK_FORCE_SUGGESTS_` = "f \item{envvar: }{R_CHECKED_CHECK_ARGS (space-separated R CMD check flags)} }} +\item{install_envvars}{\describe{ +named \code{character} vector of environment variables to use during +the package installation.\item{default: }{\preformatted{callr::rcmd_safe_env()}} +\item{option: }{checked.install_envvars} +\item{envvar: }{R_CHECKED_INSTALL_ENVVARS (evaluated if possible, raw string otherwise)} +}} + +\item{install_system_profile}{\describe{ +\code{logical} used as \code{sytem_profile} parameter passed to the \code{callr::r_bg()} +function used to install packages\item{default: }{\preformatted{FALSE}} +\item{option: }{checked.install_system_profile} +\item{envvar: }{R_CHECKED_INSTALL_SYSTEM_PROFILE (evaluated if possible, raw string otherwise)} +}} + +\item{install_user_profile}{\describe{ +value used as \code{user_profile} parameter passed to the \code{callr::r_bg()} +function used to install packages\item{default: }{\preformatted{"project"}} +\item{option: }{checked.install_user_profile} +\item{envvar: }{R_CHECKED_INSTALL_USER_PROFILE (evaluated if possible, raw string otherwise)} +}} + } } diff --git a/man/options_params.Rd b/man/options_params.Rd index 4e68647..4402e65 100644 --- a/man/options_params.Rd +++ b/man/options_params.Rd @@ -16,8 +16,14 @@ are discovered when generating results. "never" means that no errors are thrown. If "issues" then errors are emitted only on issues, whereas "potential issues" stands for error on both issues and potential issues. (Defaults to \code{"never"}, overwritable using option 'checked.results_error_on' or environment variable 'R_CHECKED_RESULTS_ERROR_ON')} +\item{install_envvars}{named \code{character} vector of environment variables to use during +the package installation. (Defaults to \code{callr::rcmd_safe_env()}, overwritable using option 'checked.install_envvars' or environment variable 'R_CHECKED_INSTALL_ENVVARS')} + \item{check_args}{\code{character} vector of args passed to the R CMD check. (Defaults to \code{c("--timings", "--ignore-vignettes", "--no-manual", "--as-cran")}, overwritable using option 'checked.check_args' or environment variable 'R_CHECKED_CHECK_ARGS')} +\item{install_system_profile}{\code{logical} used as \code{sytem_profile} parameter passed to the \code{callr::r_bg()} +function used to install packages (Defaults to \code{FALSE}, overwritable using option 'checked.install_system_profile' or environment variable 'R_CHECKED_INSTALL_SYSTEM_PROFILE')} + \item{results_keep}{character vector indicating which packages should be included in the results. "all" means that all packages are kept. If "issues" then only packages with issues identified, whereas "potential_issues" stands for keeping @@ -38,6 +44,9 @@ the R CMD check. (Defaults to \verb{c(}\emph{R_CHECK_FORCE_SUGGESTS}\verb{= "fal running checks. If \code{FALSE}, an attempt will me made to restore previous progress from the same \code{output} (Defaults to \code{NA}, overwritable using option 'checked.restore' or environment variable 'R_CHECKED_RESTORE')} +\item{install_user_profile}{value used as \code{user_profile} parameter passed to the \code{callr::r_bg()} +function used to install packages (Defaults to \code{"project"}, overwritable using option 'checked.install_user_profile' or environment variable 'R_CHECKED_INSTALL_USER_PROFILE')} + \item{tty_default_height}{deafult tty height used for the ANSI reporter. Used only if correct values could not be acquired with system('tput lines') (Defaults to \code{50}, overwritable using option 'checked.tty_default_height' or environment variable 'R_CHECKED_TTY_DEFAULT_HEIGHT')} } diff --git a/man/pkg_origin.Rd b/man/pkg_origin.Rd index 333ced9..db1a53e 100644 --- a/man/pkg_origin.Rd +++ b/man/pkg_origin.Rd @@ -3,7 +3,6 @@ \name{pkg_origin} \alias{pkg_origin} \alias{pkg_origin_repo} -\alias{pkg_origin_is_base} \alias{pkg_origin_base} \alias{pkg_origin_unknown} \alias{pkg_origin_local} @@ -15,8 +14,6 @@ pkg_origin(package, ..., .class = c()) pkg_origin_repo(package, repos, ...) -pkg_origin_is_base(package, ...) - pkg_origin_base(package, ...) pkg_origin_unknown(package, ...) diff --git a/man/plan_local_checks.Rd b/man/plan_local_checks.Rd index 34f3e14..6916c83 100644 --- a/man/plan_local_checks.Rd +++ b/man/plan_local_checks.Rd @@ -4,13 +4,21 @@ \alias{plan_local_checks} \title{Plan R CMD Checks} \usage{ -plan_local_checks(package, repos = getOption("repos")) +plan_local_checks( + package, + repos = getOption("repos"), + remotes_dependencies = TRUE +) } \arguments{ \item{package}{A path to either package, directory with packages or name of the package (details)} \item{repos}{repository used to identify packages when name is provided.} + +\item{remotes_dependencies}{A vector of length one or a named list. +Compatible with \code{\link{as_pkg_dependencies}}. Used to filter out remotes +dependencies.} } \description{ Generates a plan for running R CMD check for a specified set of packages. diff --git a/man/plan_local_install.Rd b/man/plan_local_install.Rd index 8346cb5..aed52d5 100644 --- a/man/plan_local_install.Rd +++ b/man/plan_local_install.Rd @@ -4,12 +4,24 @@ \alias{plan_local_install} \title{Plan source package installation} \usage{ -plan_local_install(package, repos = getOption("repos")) +plan_local_install( + package, + repos = getOption("repos"), + remotes_dependencies = TRUE, + INSTALL_opts = c() +) } \arguments{ \item{package}{A path to package source.} \item{repos}{repository used to identify packages when name is provided.} + +\item{remotes_dependencies}{A vector of length one or a named list. +Compatible with \code{\link{as_pkg_dependencies}}. Used to filter out remotes +dependencies.} + +\item{INSTALL_opts}{Options to set while the root package is being installed. +Check \code{\link[utils:install.packages]{utils::install.packages}} for details.} } \description{ Generates a plan for running installing a package from source. diff --git a/man/plan_rev_dep_checks.Rd b/man/plan_rev_dep_checks.Rd index a61a5a6..9708ecb 100644 --- a/man/plan_rev_dep_checks.Rd +++ b/man/plan_rev_dep_checks.Rd @@ -4,12 +4,20 @@ \alias{plan_rev_dep_checks} \title{Plan Reverse Dependency Checks} \usage{ -plan_rev_dep_checks(path, repos = getOption("repos")) +plan_rev_dep_checks( + path, + repos = getOption("repos"), + remotes_dependencies = TRUE +) } \arguments{ \item{path}{path to the package source.} \item{repos}{repository used to identify reverse dependencies.} + +\item{remotes_dependencies}{A vector of length one or a named list. +Compatible with \code{\link{as_pkg_dependencies}}. Used to filter out remotes +dependencies.} } \description{ Generates a plan for running reverse dependency check for certain diff --git a/man/reporters.Rd b/man/reporters.Rd index b2f8c13..05041c4 100644 --- a/man/reporters.Rd +++ b/man/reporters.Rd @@ -8,13 +8,21 @@ \alias{reporter_default} \title{Check checker Runner Reporters} \usage{ -reporter_ansi_tty() +reporter_ansi_tty(...) -reporter_ansi_tty2() +reporter_ansi_tty2(...) -reporter_basic_tty() +reporter_basic_tty(checks_only = FALSE, ...) -reporter_default() +reporter_default(checker = NULL) +} +\arguments{ +\item{...}{additional values which should be assigned to the reported +environment.} + +\item{checks_only}{whether basic tty reporter should report only check tasks.} + +\item{checker}{checker object required to properly derive default reporter.} } \description{ Reporters are used to configure how output is communicated while running diff --git a/man/run.Rd b/man/run.Rd index e6d7993..94cc504 100644 --- a/man/run.Rd +++ b/man/run.Rd @@ -4,7 +4,7 @@ \alias{run} \title{Run a Series of \verb{R CMD check}s} \usage{ -run(checker, ..., reporter = reporter_default()) +run(checker, ..., reporter = reporter_default(checker)) } \arguments{ \item{checker}{\code{character} or \code{checker} If a \code{character} value is diff --git a/man/task_graph.Rd b/man/task_graph.Rd index e01a62f..6a2b2df 100644 --- a/man/task_graph.Rd +++ b/man/task_graph.Rd @@ -4,7 +4,7 @@ \alias{task_graph} \title{Build task graph edges} \usage{ -task_graph(x, repos = getOption("repos"), ...) +task_graph(x, repos = getOption("repos"), dependencies = TRUE, ...) } \arguments{ \item{x}{a \code{plan} object, containing a list of related steps.} @@ -12,6 +12,9 @@ task_graph(x, repos = getOption("repos"), ...) \item{repos}{\code{repos}, as expected by \code{\link[tools:package_dependencies]{tools::package_dependencies()}} to determine package relationships.} +\item{dependencies}{A vector of length one or a named list. Compatible with +\code{\link{as_pkg_dependencies}}.} + \item{...}{params passed to helper methods.} } \value{ diff --git a/tests/testthat/_snaps/reporters.md b/tests/testthat/_snaps/reporters.md index 6806e4f..5c29a37 100644 --- a/tests/testthat/_snaps/reporters.md +++ b/tests/testthat/_snaps/reporters.md @@ -11,18 +11,18 @@ Message Checks [][install] rev.both.dependency started - [][install] rev.both.dependency finished () + [][install] rev.both.dependency finished [1/7] () [][install] pkg.ok.error started - [][install] pkg.ok.error finished () + [][install] pkg.ok.error finished [2/7] () [][install] pkg.ok.error started [][check] rev.both.ok started - [][install] pkg.ok.error finished () - [][check] rev.both.ok finished with 1 NOTE () + [][install] pkg.ok.error finished [3/7] () + [][check] rev.both.ok finished with 1 NOTE [4/7] () [][check] rev.both.error started - [][check] rev.both.error finished with 1 NOTE () + [][check] rev.both.error finished with 1 NOTE [5/7] () [][check] rev.both.ok started - [][check] rev.both.ok finished with 1 NOTE () + [][check] rev.both.ok finished with 1 NOTE [6/7] () [][check] rev.both.error started - [][check] rev.both.error finished with 1 ERROR, 1 WARNING () + [][check] rev.both.error finished with 1 ERROR, 1 WARNING [7/7] () Finished in