diff --git a/DESCRIPTION b/DESCRIPTION index 73c8931..12df7f4 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,13 +1,14 @@ Package: staged.dependencies Type: Package Title: Install R packages from Particular Git Branches -Version: 0.3.1 +Version: 0.3.1.9001 Authors@R: c( person("Adrian", "Waddell", email = "adrian.waddell@roche.com", role = c("aut", "cre")), person("Maximilian", "Mordig", email = "maximilian_oliver.mordig@roche.com", role = "aut"), person("Nikolas", "Burkoff", email = "nikolas.burkoff@roche.com", role = "aut") ) Imports: + checkmate, desc, devtools, digest, @@ -47,6 +48,7 @@ Suggests: visNetwork VignetteBuilder: knitr Collate: + 'argument_convention.R' 'caching.R' 'dependencies.R' 'dependencies_app.R' diff --git a/NAMESPACE b/NAMESPACE index 7ffdf73..1ef1914 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -15,5 +15,8 @@ export(install_deps) export(install_deps_app) export(install_deps_job) export(update_with_direct_deps) +export(verbose_sd_get) +export(verbose_sd_rm) +export(verbose_sd_set) importFrom(dplyr,"%>%") importFrom(rlang,.data) diff --git a/NEWS.md b/NEWS.md index f34304e..50eaa29 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,12 @@ +# staged.dependecies 0.3.1.9001 + +### Enhancements +* Added functions to set and remove general level of messaging as option variable `verbose_level_staged.deps`. +* Added `argument_convention.R` with default parameter definitions. + +### Miscellaneous +* Replaced internal function that checks directory existence with assertion from `checkmate` when there is no prefix. + # staged.dependecies 0.3.1 * Enhanced support of subdirectories for gitlab. diff --git a/R/argument_convention.R b/R/argument_convention.R new file mode 100644 index 0000000..6a0d84f --- /dev/null +++ b/R/argument_convention.R @@ -0,0 +1,12 @@ +#' Default Arguments +#' +#' The documentation to this function lists all the arguments in `staged.dependencies` +#' that are used repeatedly. +#' +#' @param verbose (`numeric`) \cr verbosity level, incremental; +#' (0: None, 1: packages that get installed + high-level git operations, +#' 2: includes git checkout infos) +#' +#' @keywords internal +#' @name argument_convention +NULL diff --git a/R/caching.R b/R/caching.R index 1a368fd..9c48fb4 100644 --- a/R/caching.R +++ b/R/caching.R @@ -26,9 +26,9 @@ clear_cache <- function(pattern = "*") { if (!identical(pattern, "*")) { direcs <- dir(get_packages_cache_dir()) if (length(direcs) == 0) { - message("Cache empty") + message_if_verbose("Cache empty") } else { - message("Directories remaining in cache:\n", paste(direcs, collapse = "\n")) + message_if_verbose("Directories remaining in cache:\n", paste(direcs, collapse = "\n")) } } } @@ -36,7 +36,6 @@ clear_cache <- function(pattern = "*") { # copies example config file to package settings directory # fails if copy did not work copy_config_to_storage_dir <- function() { - path <- file.path(get_storage_dir(), "config.yaml") if (!file.exists(path)) { @@ -79,9 +78,7 @@ get_active_branch_in_cache <- function(repo, host, local = FALSE) { # copies a local directory to the cache dir and commits the current state in # that cache dir, so the SHA can be added to the DESCRIPTION file # note: files in .gitignore are also available to the package locally -copy_local_repo_to_cachedir <- function(local_dir, repo, host, select_ref_rule, verbose = 0) { - check_verbose_arg(verbose) - +copy_local_repo_to_cachedir <- function(local_dir, repo, host, select_ref_rule) { local_dir <- fs::path_dir(git2r::discover_repository(local_dir)) check_dir_exists(local_dir, prefix = "Local directory: ") @@ -91,9 +88,11 @@ copy_local_repo_to_cachedir <- function(local_dir, repo, host, select_ref_rule, fs::dir_delete(repo_dir) } - if (verbose >= 1) { - message(paste("Copying local dir", local_dir, "to cache dir", repo_dir)) - } + message_if_verbose("Copying local dir ", local_dir, " to cache dir ", repo_dir, required_verbose = 2) + message_if_verbose("Copying local dir ", local_dir, " to cache dir...", + required_verbose = 1, is_equal = TRUE + ) + # file.copy copies a directory inside an existing directory # we ignore the renv sub directories as it is large (so slow), has long # path names (so causes problems on Windows) and is not needed @@ -151,12 +150,11 @@ copy_local_repo_to_cachedir <- function(local_dir, repo, host, select_ref_rule, (length(git2r::status(repo_dir)$unstaged) > 0) || (length(git2r::status(repo_dir)$untracked) > 0)) { # add all files, including untracked (all argument of git2r::commit does not do this) - if (verbose >= 2) { - message( - "Adding all of the following files: \n", - paste(utils::capture.output(git2r::status(repo_dir)), collapse = "\n") - ) - } + static_msg <- paste0( + "Adding all of the following files: \n", + paste(utils::capture.output(git2r::status(repo_dir)), collapse = "\n") + ) + message_if_verbose(static_msg, required_verbose = 2) git2r::add(repo_dir, ".") git2r::commit( repo_dir, @@ -215,7 +213,6 @@ get_hashed_repo_to_dir_mapping <- function(local_repos) { #' The packages listed there are internal packages. All other dependencies #' listed in the `DESCRIPTION` file are external dependencies. #' -#' @md #' @param repos_to_process `list` of `list(repo, host)` repos to start from #' @param ref (`character`) tag/branch to build #' @param direction (`character`) direction in which to discover packages @@ -225,23 +222,20 @@ get_hashed_repo_to_dir_mapping <- function(local_repos) { #' local rather than cloned; columns are `repo, host, directory` #' @param fallback_branch (`character`) the default branch to try to use if #' no other matches found -#' @param verbose (`numeric`) verbosity level, incremental; -#' (0: None, 1: packages that get installed + high-level git operations, -#' 2: includes git checkout infos) #' #' @return A data frame, one row per checked out repository with columns #' repo, host and cache_dir +#' +#' @keywords internal rec_checkout_internal_deps <- function(repos_to_process, ref, direction = "upstream", local_repos = get_local_pkgs_from_config(), - fallback_branch = "main", - verbose = 0) { + fallback_branch = "main") { stopifnot( is.list(repos_to_process) ) direction <- check_direction_arg_deprecated(direction) check_direction_arg(direction) - check_verbose_arg(verbose) local_repo_to_dir <- get_hashed_repo_to_dir_mapping(local_repos) rm(local_repos) @@ -268,8 +262,7 @@ rec_checkout_internal_deps <- function(repos_to_process, ref, local_repo_to_dir[[hashed_repo_and_host]], repo_and_host$repo, repo_and_host$host, select_ref_rule = function(available_refs) { determine_ref(ref, available_refs, fallback_branch = fallback_branch) - }, - verbose = verbose + } ) } else { repo_info <- checkout_repo( @@ -279,8 +272,7 @@ rec_checkout_internal_deps <- function(repos_to_process, ref, select_ref_rule = function(available_refs) { determine_ref(ref, available_refs, fallback_branch = fallback_branch) }, - must_work = (length(hashed_processed_repos) == 0), # first repo must be accessible - verbose = verbose + must_work = (length(hashed_processed_repos) == 0) # first repo must be accessible ) } @@ -312,5 +304,8 @@ rec_checkout_internal_deps <- function(repos_to_process, ref, df$accessible <- unlist(unname(hashed_repos_accessible)) df$ref <- unlist(unname(hashed_repos_refs)) df$sha <- unlist(unname(hashed_repos_shas)) + + message_if_verbose("Current cache directory: ", fs::path_norm(get_packages_cache_dir())) + return(df) } diff --git a/R/dependencies.R b/R/dependencies.R index e8909fc..18dd732 100644 --- a/R/dependencies.R +++ b/R/dependencies.R @@ -12,22 +12,20 @@ #' be character of the form `openpharma/stageddeps.food@https://github.com` #' If host is not included in the string then the default `https://github.com` #' is assumed. -#' @param project_type (`character`) See `project` argument +#' @param project_type (`character`) See `project` argument. #' @param ref (`character`) git branch (or tag) inferred from the #' branch of the project if not provided; warning if not consistent with #' current branch of project. If `project_type` is not `local` then this argument -#' must be provided +#' must be provided. #' @param local_repos (`data.frame`) repositories that should be taken from -#' local file system rather than cloned; columns are `repo, host, directory` +#' local file system rather than cloned; columns are `repo, host, directory`. #' @param direction (`character`) direction in which to discover packages #' either "upstream","downstream" or "all". #' @param fallback_branch (`character`) the default branch to try to use if -#' no other matches found +#' no other matches found. It defaults to `"main"`. #' @param renv_profile (`character`) the name of the renv profile of the `renv.lock` files #' to be included from the repos. The standard `renv.lock` file uses the default `NULL` argument here. -#' @param verbose (`numeric`) verbosity level, incremental; -#' (0: None, 1: packages that get installed + high-level git operations, -#' 2: includes git checkout infos) +#' @inheritParams argument_convention #' @return `dependency_structure` An S3 object with the following items: #' \describe{ #' \item{project}{`project` argument used to create @@ -49,8 +47,6 @@ #' \item{renv_files}{`named list` containing the json of the renv.lock files for the chosen profile for #' each repo. An entry to the list is `NULL` if a repos does not have the required lock file} #' } -#' @md -#' @export #' @examples #' \dontrun{ #' dependency_table(verbose = 1) @@ -66,6 +62,7 @@ #' print(x) #' plot(x) #' } +#' @export dependency_table <- function(project = ".", project_type = c("local", "repo@host")[1], ref = NULL, @@ -74,28 +71,29 @@ dependency_table <- function(project = ".", fallback_branch = "main", renv_profile = NULL, verbose = 1) { - # validate arguments stopifnot(is.data.frame(local_repos) || is.null(local_repos)) - check_verbose_arg(verbose) direction <- check_direction_arg_deprecated(direction) check_direction_arg(direction) stopifnot(project_type %in% c("local", "repo@host")) stopifnot(rlang::is_scalar_character(fallback_branch)) + if (verbose != 1) { + checkmate::assert_int(verbose, lower = 0, upper = 2) + verbose_sd_set(verbose) + } + if (project_type == "repo@host" && (is.null(ref) || nchar(ref) == 0)) { stop("For non-local projects the (branch/tag) must be specified") } if (project_type == "local") { - check_dir_exists(project) + checkmate::assert_directory_exists(project) error_if_stageddeps_inexistent(project) # infer ref if not given if (is.null(ref) || nchar(ref) == 0) { ref <- infer_ref_from_branch(project) } - } - if (project_type == "local") { # take local version of project (rather than remote) local_repos <- add_project_to_local_repos(project, local_repos) repo_deps_info <- get_yaml_deps_info(project) @@ -107,7 +105,7 @@ dependency_table <- function(project = ".", ), fallback_branch ) - repo_to_process <- list(repo_deps_info$current_repo) + repo_to_process <- list(repo_deps_info$current_repo) # This list will be updated with the deps } else { repo_to_process <- list(parse_remote_project(project)) } @@ -117,8 +115,7 @@ dependency_table <- function(project = ".", internal_deps <- rec_checkout_internal_deps( repo_to_process, ref, direction = direction, - local_repos = local_repos, fallback_branch = fallback_branch, - verbose = verbose + local_repos = local_repos, fallback_branch = fallback_branch ) internal_deps$package_name[internal_deps$accessible] <- @@ -218,7 +215,7 @@ print.dependency_structure <- function(x, ...) { if (!all(table$installable)) { table$package_name <- paste0(table$package_name, ifelse(table$installable, "", "*")) - cat( + warning( "packages denoted with '*' cannot be installed as either they or one of their internal", "dependencies is not accessible\n" ) @@ -235,7 +232,6 @@ print.dependency_structure <- function(x, ...) { #' @importFrom rlang .data #' @export plot.dependency_structure <- function(x, y, ...) { - # construct visNetwork graph require_pkgs("visNetwork") # todo: put branch below node: https://github.com/almende/vis/issues/3436 @@ -349,13 +345,12 @@ plot.dependency_structure <- function(x, y, ...) { #' list will be considered and their dependencies installed if needed (advanced usage only). #' @param dry (`logical`) dry run that outputs what would happen without actually #' doing it. -#' @param verbose verbosity level, incremental; from 0 (none) to 2 (high) #' @param ... Additional args passed to [remotes::install_deps()]. Ignored #' if inside an `renv` environment. +#' @inheritParams argument_convention #' #' @return `data.frame` of performed actions #' -#' @export #' @seealso determine_branch #' #' @examples @@ -367,6 +362,7 @@ plot.dependency_structure <- function(x, y, ...) { #' # install all dependencies #' install_deps(x, install_direction = "all") #' } +#' @export install_deps <- function(dep_structure, install_project = TRUE, install_direction = "upstream", @@ -384,6 +380,11 @@ install_deps <- function(dep_structure, stop("Invalid install_direction argument for this dependency object") } + if (verbose != 1) { + checkmate::assert_int(verbose, lower = 0, upper = 2) + verbose_sd_set(verbose) + } + # get the packages to install pkg_df <- dep_structure$table @@ -404,8 +405,9 @@ install_deps <- function(dep_structure, install_external_deps = install_external_deps, upgrade = upgrade, internal_pkg_deps = dep_structure$table$package_name, - verbose = verbose, ... + ... ) + pkg_actions } @@ -449,6 +451,11 @@ check_downstream <- function(dep_structure, stop("Invalid dependency table - downstream dependencies must be have been calculated") } + if (verbose != 1) { + checkmate::assert_int(verbose, lower = 0, upper = 2) + verbose_sd_set(verbose) + } + # get the packages to install pkg_df <- dep_structure$table @@ -473,7 +480,7 @@ check_downstream <- function(dep_structure, upgrade = upgrade, internal_pkg_deps = dep_structure$table$package_name, rcmd_args = list(check = check_args), - verbose = verbose, ... + ... ) pkg_actions @@ -509,9 +516,7 @@ update_with_direct_deps <- function(dep_structure) { #' Build, check and install internal dependencies #' - #' -#' @md #' @inheritParams install_deps #' @inheritDotParams install_deps #' @param steps (`character` vector) subset of "build", "check", "install"; @@ -524,13 +529,13 @@ update_with_direct_deps <- function(dep_structure) { #' @return list with entries #' - artifact_dir: `artifact_dir` directory with log files #' - pkg_actions: `data.frame` of performed actions -#' @export #' @examples #' \dontrun{ #' x <- dependency_table(project = ".", verbose = 1) #' build_check_install(x, steps = c("build", "check"), verbose = 1) #' build_check_install(x, artifact_dir = "../output") #' } +#' @export build_check_install <- function(dep_structure, install_direction = "all", steps = c("build", "check", "install"), @@ -546,6 +551,10 @@ build_check_install <- function(dep_structure, methods::is(dep_structure, "dependency_structure"), is.logical(dry) ) + if (verbose != 1) { + checkmate::assert_int(verbose, lower = 0, upper = 2) + verbose_sd_set(verbose) + } install_direction <- check_direction_arg_deprecated(install_direction) if (dep_structure$direction != "all" && dep_structure$direction != install_direction) { @@ -579,7 +588,7 @@ build_check_install <- function(dep_structure, internal_pkg_deps = dep_structure$table$package_name, rcmd_args = rcmd_args, artifact_dir = artifact_dir, - verbose = verbose, ... + ... ) return(list(artifact_dir = artifact_dir, pkg_actions = pkg_actions)) @@ -676,7 +685,6 @@ check_yamls_consistent <- function(dep_structure, skip_if_missing_yaml = FALSE) #' which fields of the DESCRIPTION file of the internal packages should be included. Default: #' `c("Depends", "Imports", "LinkingTo")` #' @inheritParams build_check_install -#' @md #' @return A vector of 'external' R packages required to install #' the selected 'internal' packages, ordered by install order (unless `from_external_dependencies` #' does not include `"Depends"`, `"Imports"` and `"LinkingTo"`). The core R packages diff --git a/R/dependencies_app.R b/R/dependencies_app.R index c97c8c1..dc44c67 100644 --- a/R/dependencies_app.R +++ b/R/dependencies_app.R @@ -21,22 +21,28 @@ #' @param run_gadget (`logical`) whether to run the app as a gadget #' @param run_as_job (`logical`) whether to run the installation as an RStudio job. #' @inheritParams install_deps -#' @export #' @return `shiny.app` or value returned by app (executed as a gadget) #' @examples #' \dontrun{ #' install_deps_app("openpharma/stageddeps.food") #' } +#' @export install_deps_app <- function(default_repo = NULL, default_host = "https://github.com", default_ref = "main", fallback_branch = "main", run_gadget = TRUE, run_as_job = TRUE, - verbose = 1, install_external_deps = TRUE, + verbose = 1, + install_external_deps = TRUE, renv_profile = NULL, upgrade = "never", ...) { require_pkgs(c("shiny", "miniUI", "visNetwork")) + if (verbose != 1) { + checkmate::assert_int(verbose, lower = 0, upper = 2) + verbose_sd_set(verbose) + } + app <- shiny::shinyApp( ui = function() { miniUI::miniPage( @@ -80,9 +86,9 @@ install_deps_app <- function(default_repo = NULL, dep_table_rv(NULL) error_rv(NULL) - message_if_verbose("Computing dependency structure for ref ", - input$ref, " starting from project ", paste(input$repo, input$host, sep = "@"), - verbose = verbose + message_if_verbose( + "Computing dependency structure for ref ", + input$ref, " starting from project ", paste(input$repo, input$host, sep = "@") ) x <- tryCatch( diff --git a/R/dependencies_helper.R b/R/dependencies_helper.R index 44866e0..a280632 100644 --- a/R/dependencies_helper.R +++ b/R/dependencies_helper.R @@ -22,24 +22,24 @@ #' to the `R CMD` commands #' @param artifact_dir directory to store log files, only when actions include #' `build`; action `test` only outputs to the console -#' @param verbose verbosity level, incremental - from 0 (none) to 2 (high) #' @param ... Additional args passed to [remotes::install_deps()] Ignored #' if inside an `renv` environment. +#' +#' @keywords internal run_package_actions <- function(pkg_actions, internal_pkg_deps, dry = FALSE, install_external_deps = TRUE, upgrade = "never", rcmd_args = NULL, artifact_dir = NULL, - verbose = 0, ...) { + ...) { all_actions <- unique(unlist(pkg_actions$actions)) stopifnot(all(all_actions %in% c("test", "build", "check", "install"))) - check_verbose_arg(verbose) rcmdcheck_outputs <- list() if (nrow(pkg_actions) == 0) { - message_if_verbose("No packages to process!", verbose = verbose) + message_if_verbose("No packages to process!") return(pkg_actions) } @@ -55,9 +55,9 @@ run_package_actions <- function(pkg_actions, internal_pkg_deps, ) } - message_if_verbose("Processing packages in order: ", - toString(pkg_actions$package_name), - verbose = verbose + message_if_verbose( + "Processing packages in order: ", + toString(pkg_actions$package_name) ) for (idx in seq_along(pkg_actions$cache_dir)) { @@ -65,9 +65,9 @@ run_package_actions <- function(pkg_actions, internal_pkg_deps, actions <- pkg_actions$actions[idx] if (!pkg_actions$installable[idx]) { - message_if_verbose("skipping package ", pkg_actions$package_name[idx], - " as it (or one of its upstream dependencies) is not accessible", - verbose = verbose + message_if_verbose( + "Skipping package ", pkg_actions$package_name[idx], + " as it (or one of its upstream dependencies) is not accessible" ) next } @@ -104,7 +104,7 @@ run_package_actions <- function(pkg_actions, internal_pkg_deps, stop("Tests for package in directory ", cache_dir, " failed") } } else { - message_if_verbose("No tests found for package in directory ", cache_dir, verbose = verbose) + message_if_verbose("No tests found for package in directory ", cache_dir) } } @@ -158,11 +158,11 @@ run_package_actions <- function(pkg_actions, internal_pkg_deps, } } } else { # dry run - message_if_verbose(cat_nl("(Dry run) Skipping", toString(actions), "of", cache_dir), verbose = verbose) + message_if_verbose(cat_nl("(Dry run) Skipping ", toString(actions), " of ", cache_dir)) } } - message_if_verbose("Processed packages in order: ", toString(pkg_actions$package_name), verbose = verbose) + message_if_verbose("Processed packages in order: ", toString(pkg_actions$package_name)) # output rcmdcheck outputs if (!rlang::is_empty(rcmdcheck_outputs)) { @@ -185,7 +185,7 @@ add_project_to_local_repos <- function(project, local_repos) { stopifnot( is.data.frame(local_repos) || is.null(local_repos) ) - check_dir_exists(project) + checkmate::assert_directory_exists(project) repo_deps_info <- get_yaml_deps_info(project) rbind( @@ -272,7 +272,6 @@ get_true_deps_graph <- function(pkgs_df, # for inaccessible packages we need to get their upstream # dependencies from yaml files of accessible repos if (!all(pkgs_df$accessible)) { - # for each accessible package for (idx in seq_len(nrow(pkgs_df))) { if (!pkgs_df$accessible[idx]) { diff --git a/R/git_tools.R b/R/git_tools.R index b93686b..2718d06 100644 --- a/R/git_tools.R +++ b/R/git_tools.R @@ -26,7 +26,8 @@ get_current_branch <- function(git_repo) { # staged_dep has previously checked out a version where ref = <>) check_only_remote_branches <- function(git_repo, remote_name) { all_branches <- names(git2r::branches(git_repo)) - stopifnot(all(vapply(all_branches, function(x) startsWith(x, paste0(remote_name, "/")) || startsWith(x, "staged_dep_tag_"), logical(1)))) + stopifnot(all(vapply(all_branches, function(x) startsWith(x, paste0(remote_name, "/")) || startsWith(x, "staged_dep_tag_"), + logical(1)))) } # clones the repo and only keeps remote branches @@ -34,15 +35,14 @@ check_only_remote_branches <- function(git_repo, remote_name) { # no longer there # select_ref_rule is a function that is given the available refs # and selects one of them -# if must_work is TRUE then error is thrown if repo is not accessible, if FALSE then wantning is thrown +# if must_work is TRUE then error is thrown if repo is not accessible, if FALSE then warning is thrown # verbose level: 0: none, 1: print high-level git operations, 2: print git clone detailed messages etc. # returns: list of repo_dir and checked out branch/ref (according to branch rule) -checkout_repo <- function(repo_dir, repo_url, select_ref_rule, token_envvar = NULL, must_work = FALSE, verbose = 0) { +checkout_repo <- function(repo_dir, repo_url, select_ref_rule, token_envvar = NULL, must_work = FALSE) { stopifnot( is.function(select_ref_rule), endsWith(repo_url, ".git") ) - check_verbose_arg(verbose) creds <- if (is.null(token_envvar)) { NULL @@ -51,15 +51,17 @@ checkout_repo <- function(repo_dir, repo_url, select_ref_rule, token_envvar = NU } if (!dir.exists(repo_dir)) { stopifnot(is_non_empty_char(repo_url)) - if (verbose >= 1) { - message(paste("clone", repo_url, "to directory", repo_dir)) - } + + message_if_verbose("clone ", repo_url, " to directory ", repo_dir, required_verbose = 2) + message_if_verbose("clone ", repo_url, " to cache directory...", + required_verbose = 1, is_equal = TRUE + ) cloned_repo <- tryCatch( { git_repo <- git2r::clone( url = repo_url, local_path = repo_dir, - credentials = creds, progress = verbose >= 2 + credentials = creds, progress = verbose_sd_get() >= 2 ) }, error = function(e) { @@ -120,16 +122,21 @@ checkout_repo <- function(repo_dir, repo_url, select_ref_rule, token_envvar = NU # todo: on.exit if unsuccessful } else { - if (verbose >= 1) { - message(paste("fetch", git2r::remote_url(repo_dir), "in directory", repo_dir)) - } + message_if_verbose("fetch ", git2r::remote_url(repo_dir), " in directory ", repo_dir, required_verbose = 2) + message_if_verbose("fetch ", git2r::remote_url(repo_dir), " in cache directory...", + required_verbose = 1, is_equal = TRUE + ) git_repo <- git2r::repository(repo_dir) # prune (remove) remote branches that were deleted from remote git2r::config(git_repo, remote.origin.prune = "true") tryCatch( { - git2r::fetch(git_repo, name = get_remote_name(git_repo, repo_url), credentials = creds, verbose = verbose >= 2) + git2r::fetch(git_repo, + name = get_remote_name(git_repo, repo_url), + credentials = creds, + verbose = verbose_sd_get() >= 2 + ) }, error = function(cond) { warning( @@ -151,16 +158,24 @@ checkout_repo <- function(repo_dir, repo_url, select_ref_rule, token_envvar = NU } branch <- paste0(get_remote_name(git_repo, repo_url), "/", selected_ref) - if (verbose >= 1) { - message(paste(" - checkout branch", branch, "in directory", repo_dir)) - } + + message_if_verbose(" - checkout branch ", branch, " in directory ", repo_dir, required_verbose = 2) + message_if_verbose(" - checkout branch ", branch, " in cache directory...", + required_verbose = 1, is_equal = TRUE + ) + git2r::checkout(git_repo, branch = branch, force = TRUE) } else if (attr(selected_ref, "type") == "tag") { - if (verbose >= 1) { - message(paste(" - checkout tag", selected_ref, "in directory", repo_dir)) - git2r::branch_create(commit = git2r::commits(repo = repo_dir, ref = selected_ref, n = 1)[[1]], force = TRUE, name = paste0("staged_dep_tag_", selected_ref)) - git2r::checkout(git_repo, branch = paste0("staged_dep_tag_", selected_ref), force = TRUE) - } + message_if_verbose(" - checkout tag ", selected_ref, " in directory ", repo_dir, required_verbose = 2) + message_if_verbose(" - checkout tag ", selected_ref, " in cache directory...", + required_verbose = 1, is_equal = TRUE + ) + + git2r::branch_create( + commit = git2r::commits(repo = repo_dir, ref = selected_ref, n = 1)[[1]], + force = TRUE, name = paste0("staged_dep_tag_", selected_ref) + ) + git2r::checkout(git_repo, branch = paste0("staged_dep_tag_", selected_ref), force = TRUE) } else { stop("The selected reference should have a type attribute as 'branch' or 'tag'") } @@ -174,7 +189,6 @@ checkout_repo <- function(repo_dir, repo_url, select_ref_rule, token_envvar = NU # Install the external deps required for a package # does not install dependencies that appear in `internal_pkg_deps` install_external_deps <- function(repo_dir, internal_pkg_deps, ...) { - # `remotes::install_deps` (and renv::install) # only makes use of the package DESCRIPTION file # So we create a temp directory containing this file and then call this function @@ -210,7 +224,6 @@ install_external_deps <- function(repo_dir, internal_pkg_deps, ...) { # function to get the remote name (e.g. origin) which matches # the url given in the staged.deps yaml file get_remote_name <- function(git_repo, repo_url) { - # remove the https:// and .git from repo_url repo_url <- gsub("^.+://|.git$", "", repo_url, perl = TRUE) @@ -240,9 +253,8 @@ get_remote_name <- function(git_repo, repo_url) { #' @param repo_dir directory of repo #' @param ... Additional args passed to `remotes::install_deps`. Note `upgrade` #' is set to "never" and shouldn't be passed into this function. -install_repo_add_sha <- function(repo_dir, - ...) { - check_dir_exists(repo_dir) +install_repo_add_sha <- function(repo_dir, ...) { + checkmate::assert_directory_exists(repo_dir) read_dcf <- function(path) { fields <- colnames(read.dcf(path)) diff --git a/R/graph_methods.R b/R/graph_methods.R index 4f91d35..38e4c3a 100644 --- a/R/graph_methods.R +++ b/R/graph_methods.R @@ -67,7 +67,6 @@ topological_sort <- function(graph) { # get the descendants (all children) of node and their distances, # given list mapping parent to children get_descendants_distance <- function(parents_to_children, starting_node) { - # implement BFS nodes_to_treat <- c(starting_node) # ordered queue distances <- list() diff --git a/R/ref_strategy.R b/R/ref_strategy.R index 36e33c6..741528b 100644 --- a/R/ref_strategy.R +++ b/R/ref_strategy.R @@ -112,13 +112,13 @@ determine_ref <- function(ref, available_refs, fallback_branch = "main", branch_ # infer the ref if it is null infer_ref_from_branch <- function(project = ".") { - check_dir_exists(project) + checkmate::assert_directory_exists(project) return(get_current_branch(project)) } check_ref_consistency <- function(ref, project = ".", remote_name = "origin", fallback_branch = "main") { - check_dir_exists(project) + checkmate::assert_directory_exists(project) current_branch <- get_current_branch(project) # if in detached HEAD then we ignore the ref_consistency check (i.e. so gitlab # automation does not throw a warning) diff --git a/R/utils.R b/R/utils.R index febda53..169192a 100644 --- a/R/utils.R +++ b/R/utils.R @@ -1,14 +1,69 @@ # cat pasted arguments + new line -cat_nl <- function(...) cat(paste0(paste(...), "\n")) +cat_nl <- function(...) { + cat(paste0(paste(...), "\n")) +} # cat_nl("fff", "gg") +#' Set staged.dependencies verbosity +#' +#' @description +#' Functions to set and remove the option parameter `verbose_level_staged.deps`. +#' It can assume integer values between `c(0, 1, 2)`. This will set this variable +#' as an option with [options()] and [getOption()]. +#' +#' @inheritParams argument_convention +#' +#' @examples +#' verbose_sd_set(2) +#' verbose_sd_get() # 2, the inserted value +#' verbose_sd_rm() +#' verbose_sd_get() # 1, the default +#' +#' @export +#' @name verbose_sd_option +verbose_sd_set <- function(verbose = 1) { + options("verbose_level_staged.deps" = verbose) +} +#' @name verbose_sd_option +#' @export +verbose_sd_get <- function() { + ret <- getOption("verbose_level_staged.deps") + if (is.null(ret)) { + 1 # Default + } else { + ret + } +} +#' @export +#' @name verbose_sd_option +verbose_sd_rm <- function() { + if (is.null(getOption("verbose_level_staged.deps"))) { + stop("No verbose_level_staged.deps to remove in general environment.") + } else { + options("verbose_level_staged.deps" = NULL) # Reset + } +} + # output message if verbose argument is at least required_verbose -message_if_verbose <- function(..., verbose, required_verbose = 1) { - if (verbose >= required_verbose) { +message_if_verbose <- function(..., verbose = NULL, required_verbose = 1, is_equal = FALSE) { + if (is.null(verbose)) { + verb <- verbose_sd_get() + } + # Should it be verbose equal to required or >= ? + if (moe_sd(verb, required_verbose, is_equal)) { message(...) } } +# Helper fnc - major or equal +moe_sd <- function(verb, req_verb, is_equal) { + if (isTRUE(is_equal)) { + verb == req_verb + } else { + verb >= req_verb + } +} + extract_str_field <- function(lst, field) { vapply(lst, function(x) x[[field]], character(1)) } @@ -98,7 +153,6 @@ check_dir_exists <- function(direc, prefix = "") { # validate the contents of the yaml file (after conversion into R) # return NULL if valid throw error if not validate_staged_deps_yaml <- function(content, file_name = "") { - # A simplified schema object to capture the schema for the yaml file # each entry of the list contains the top level field, with their name # whether they can be NULL and their subfields. If array is TRUE then each element @@ -140,7 +194,6 @@ validate_staged_deps_yaml <- function(content, file_name = "") { # next check the contents of the fields is as expected lapply(required_schema, function(field) { - # extract the contents for this field sub_content <- content[[field$name]] diff --git a/R/zzz.R b/R/zzz.R index 298699c..6e1a3ab 100644 --- a/R/zzz.R +++ b/R/zzz.R @@ -27,7 +27,6 @@ set_storage_dir <- function(storage_dir) { } .onLoad <- function(libname, pkgname) { - storage_dir <- getOption( "staged.dependencies._storage_dir", path.expand("~/.staged.dependencies") diff --git a/man/argument_convention.Rd b/man/argument_convention.Rd new file mode 100644 index 0000000..1497481 --- /dev/null +++ b/man/argument_convention.Rd @@ -0,0 +1,15 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/argument_convention.R +\name{argument_convention} +\alias{argument_convention} +\title{Default Arguments} +\arguments{ +\item{verbose}{(\code{numeric}) \cr verbosity level, incremental; +(0: None, 1: packages that get installed + high-level git operations, +2: includes git checkout infos)} +} +\description{ +The documentation to this function lists all the arguments in \code{staged.dependencies} +that are used repeatedly. +} +\keyword{internal} diff --git a/man/build_check_install.Rd b/man/build_check_install.Rd index 088e7dd..5005624 100644 --- a/man/build_check_install.Rd +++ b/man/build_check_install.Rd @@ -50,7 +50,9 @@ list will be considered and their dependencies installed if needed (advanced usa \item{dry}{(\code{logical}) dry run that outputs what would happen without actually doing it.} -\item{verbose}{verbosity level, incremental; from 0 (none) to 2 (high)} +\item{verbose}{(\code{numeric}) \cr verbosity level, incremental; +(0: None, 1: packages that get installed + high-level git operations, +2: includes git checkout infos)} \item{...}{ Arguments passed on to \code{\link[=install_deps]{install_deps}} diff --git a/man/check_downstream.Rd b/man/check_downstream.Rd index 0fcc718..56c50d2 100644 --- a/man/check_downstream.Rd +++ b/man/check_downstream.Rd @@ -44,7 +44,9 @@ list will be considered and their dependencies installed if needed (advanced usa \item{dry}{(\code{logical}) dry run that outputs what would happen without actually doing it.} -\item{verbose}{verbosity level, incremental; from 0 (none) to 2 (high)} +\item{verbose}{(\code{numeric}) \cr verbosity level, incremental; +(0: None, 1: packages that get installed + high-level git operations, +2: includes git checkout infos)} \item{...}{ Arguments passed on to \code{\link[=install_deps]{install_deps}} diff --git a/man/check_downstream_job.Rd b/man/check_downstream_job.Rd index 158a531..1dd282d 100644 --- a/man/check_downstream_job.Rd +++ b/man/check_downstream_job.Rd @@ -19,7 +19,7 @@ be character of the form \verb{openpharma/stageddeps.food@https://github.com} If host is not included in the string then the default \verb{https://github.com} is assumed.} -\item{verbose}{(\code{numeric}) verbosity level, incremental; +\item{verbose}{(\code{numeric}) \cr verbosity level, incremental; (0: None, 1: packages that get installed + high-level git operations, 2: includes git checkout infos)} diff --git a/man/dependency_table.Rd b/man/dependency_table.Rd index f1dad36..35132d4 100644 --- a/man/dependency_table.Rd +++ b/man/dependency_table.Rd @@ -23,26 +23,26 @@ be character of the form \verb{openpharma/stageddeps.food@https://github.com} If host is not included in the string then the default \verb{https://github.com} is assumed.} -\item{project_type}{(\code{character}) See \code{project} argument} +\item{project_type}{(\code{character}) See \code{project} argument.} \item{ref}{(\code{character}) git branch (or tag) inferred from the branch of the project if not provided; warning if not consistent with current branch of project. If \code{project_type} is not \code{local} then this argument -must be provided} +must be provided.} \item{local_repos}{(\code{data.frame}) repositories that should be taken from -local file system rather than cloned; columns are \verb{repo, host, directory}} +local file system rather than cloned; columns are \verb{repo, host, directory}.} \item{direction}{(\code{character}) direction in which to discover packages either "upstream","downstream" or "all".} \item{fallback_branch}{(\code{character}) the default branch to try to use if -no other matches found} +no other matches found. It defaults to \code{"main"}.} \item{renv_profile}{(\code{character}) the name of the renv profile of the \code{renv.lock} files to be included from the repos. The standard \code{renv.lock} file uses the default \code{NULL} argument here.} -\item{verbose}{(\code{numeric}) verbosity level, incremental; +\item{verbose}{(\code{numeric}) \cr verbosity level, incremental; (0: None, 1: packages that get installed + high-level git operations, 2: includes git checkout infos)} } diff --git a/man/install_deps.Rd b/man/install_deps.Rd index 118d796..545945a 100644 --- a/man/install_deps.Rd +++ b/man/install_deps.Rd @@ -43,7 +43,9 @@ list will be considered and their dependencies installed if needed (advanced usa \item{dry}{(\code{logical}) dry run that outputs what would happen without actually doing it.} -\item{verbose}{verbosity level, incremental; from 0 (none) to 2 (high)} +\item{verbose}{(\code{numeric}) \cr verbosity level, incremental; +(0: None, 1: packages that get installed + high-level git operations, +2: includes git checkout infos)} \item{...}{Additional args passed to \code{\link[remotes:install_deps]{remotes::install_deps()}}. Ignored if inside an \code{renv} environment.} diff --git a/man/install_deps_app.Rd b/man/install_deps_app.Rd index 6f9ea04..6e0b066 100644 --- a/man/install_deps_app.Rd +++ b/man/install_deps_app.Rd @@ -38,7 +38,9 @@ no other matches found} \item{run_as_job}{(\code{logical}) whether to run the installation as an RStudio job.} -\item{verbose}{verbosity level, incremental; from 0 (none) to 2 (high)} +\item{verbose}{(\code{numeric}) \cr verbosity level, incremental; +(0: None, 1: packages that get installed + high-level git operations, +2: includes git checkout infos)} \item{install_external_deps}{logical to describe whether to install external dependencies of package using \code{\link[remotes:install_deps]{remotes::install_deps()}} (or \code{\link[renv:install]{renv::install()}} if diff --git a/man/install_deps_job.Rd b/man/install_deps_job.Rd index 72ead69..0d5626e 100644 --- a/man/install_deps_job.Rd +++ b/man/install_deps_job.Rd @@ -20,9 +20,9 @@ be character of the form \verb{openpharma/stageddeps.food@https://github.com} If host is not included in the string then the default \verb{https://github.com} is assumed.} -\item{project_type}{(\code{character}) See \code{project} argument} +\item{project_type}{(\code{character}) See \code{project} argument.} -\item{verbose}{(\code{numeric}) verbosity level, incremental; +\item{verbose}{(\code{numeric}) \cr verbosity level, incremental; (0: None, 1: packages that get installed + high-level git operations, 2: includes git checkout infos)} diff --git a/man/rec_checkout_internal_deps.Rd b/man/rec_checkout_internal_deps.Rd index 65421a1..633a609 100644 --- a/man/rec_checkout_internal_deps.Rd +++ b/man/rec_checkout_internal_deps.Rd @@ -10,8 +10,7 @@ rec_checkout_internal_deps( ref, direction = "upstream", local_repos = get_local_pkgs_from_config(), - fallback_branch = "main", - verbose = 0 + fallback_branch = "main" ) } \arguments{ @@ -28,10 +27,6 @@ local rather than cloned; columns are \verb{repo, host, directory}} \item{fallback_branch}{(\code{character}) the default branch to try to use if no other matches found} - -\item{verbose}{(\code{numeric}) verbosity level, incremental; -(0: None, 1: packages that get installed + high-level git operations, -2: includes git checkout infos)} } \value{ A data frame, one row per checked out repository with columns @@ -45,3 +40,4 @@ and downstream packages are listed there. The packages listed there are internal packages. All other dependencies listed in the \code{DESCRIPTION} file are external dependencies. } +\keyword{internal} diff --git a/man/run_package_actions.Rd b/man/run_package_actions.Rd index 326c1fa..c4cb537 100644 --- a/man/run_package_actions.Rd +++ b/man/run_package_actions.Rd @@ -12,7 +12,6 @@ run_package_actions( upgrade = "never", rcmd_args = NULL, artifact_dir = NULL, - verbose = 0, ... ) } @@ -47,11 +46,10 @@ to the \verb{R CMD} commands} \item{artifact_dir}{directory to store log files, only when actions include \code{build}; action \code{test} only outputs to the console} -\item{verbose}{verbosity level, incremental - from 0 (none) to 2 (high)} - \item{...}{Additional args passed to \code{\link[remotes:install_deps]{remotes::install_deps()}} Ignored if inside an \code{renv} environment.} } \description{ Run an action on the packages } +\keyword{internal} diff --git a/man/verbose_sd_option.Rd b/man/verbose_sd_option.Rd new file mode 100644 index 0000000..cad67f7 --- /dev/null +++ b/man/verbose_sd_option.Rd @@ -0,0 +1,32 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/utils.R +\name{verbose_sd_option} +\alias{verbose_sd_option} +\alias{verbose_sd_set} +\alias{verbose_sd_get} +\alias{verbose_sd_rm} +\title{Set staged.dependencies verbosity} +\usage{ +verbose_sd_set(verbose = 1) + +verbose_sd_get() + +verbose_sd_rm() +} +\arguments{ +\item{verbose}{(\code{numeric}) \cr verbosity level, incremental; +(0: None, 1: packages that get installed + high-level git operations, +2: includes git checkout infos)} +} +\description{ +Functions to set and remove the option parameter \code{verbose_level_staged.deps}. +It can assume integer values between \code{c(0, 1, 2)}. This will set this variable +as an option with \code{\link[=options]{options()}} and \code{\link[=getOption]{getOption()}}. +} +\examples{ +verbose_sd_set(2) +verbose_sd_get() # 2, the inserted value +verbose_sd_rm() +verbose_sd_get() # 1, the default + +} diff --git a/tests/testthat/test-caching.R b/tests/testthat/test-caching.R index dcdf4db..18f6670 100644 --- a/tests/testthat/test-caching.R +++ b/tests/testthat/test-caching.R @@ -33,7 +33,6 @@ test_that("clear_cache", { test_that("rec_checkout_internal_deps works (with mocking checkout)", { - # mock checkout_repo by copying the appropriate directory to the repo_dir directory mockery::stub(rec_checkout_internal_deps, "checkout_repo", function(repo_dir, repo_url, select_ref_rule, ...) { repo_name <- basename(repo_url) @@ -54,7 +53,7 @@ test_that("rec_checkout_internal_deps works (with mocking checkout)", { capture.output(rec_checkout_internal_deps( list(list(repo = "openpharma/stageddeps.food", host = "https://github.com", subdir = ".")), "unittest_branch1", - direction = c("upstream"), local_repos = NULL, fallback_branch = "not_exist", verbose = 0 + direction = c("upstream"), local_repos = NULL, fallback_branch = "not_exist" )), regexp = "Available refs .* must include at least one of 'unittest_branch1, not_exist'" ) @@ -62,7 +61,7 @@ test_that("rec_checkout_internal_deps works (with mocking checkout)", { output <- capture.output(res <- rec_checkout_internal_deps( list(list(repo = "openpharma/stageddeps.food", host = "https://github.com", subdir = ".")), "fix1@main", - direction = c("upstream"), local_repos = NULL, verbose = 0 + direction = c("upstream"), local_repos = NULL )) # check mocked functions were called in correct order @@ -108,7 +107,6 @@ test_that("rec_checkout_internal_deps works (with mocking checkout)", { }) test_that("rec_checkout_internal_deps works for inaccessible repos (with mocking checkout)", { - # mock checkout_repo by copying the appropriate directory to the repo_dir directory # but stageddeps.water is not accessible mockery::stub(rec_checkout_internal_deps, "checkout_repo", function(repo_dir, repo_url, select_ref_rule, ...) { @@ -134,7 +132,7 @@ test_that("rec_checkout_internal_deps works for inaccessible repos (with mocking res <- rec_checkout_internal_deps( list(list(repo = "openpharma/stageddeps.food", host = "https://github.com", subdir = ".")), "main", - local_repos = NULL, verbose = 0, direction = "all" + local_repos = NULL, direction = "all" ) # we should not see garden and water should not be accessible @@ -187,6 +185,7 @@ test_that("copy_local_repo_to_cachedir works", { git2r::remote_set_url(repo_dir, name = "origin", url = "https://github.com/openpharma/stageddeps.food.git") + verbose_sd_set(2) # ckeck all files (from above) are added to the git commit in a local cache dir with_tmp_cachedir({ res <- expect_message( @@ -195,8 +194,7 @@ test_that("copy_local_repo_to_cachedir works", { repo = "openpharma/stageddeps.food", host = "https://github.com", select_ref_rule = function(available_refs) { structure("main", type = "branch") - }, - verbose = 2 + } ), regexp = "Adding all of the following", fixed = TRUE ) @@ -205,6 +203,7 @@ test_that("copy_local_repo_to_cachedir works", { expect_equal(res$ref, "local (main)") expect_true(git_status_clean(res$dir)) }) + verbose_sd_rm() unlink(repo_dir, recursive = TRUE) }) diff --git a/tests/testthat/test-dependencies.R b/tests/testthat/test-dependencies.R index 1386fb3..efee735 100644 --- a/tests/testthat/test-dependencies.R +++ b/tests/testthat/test-dependencies.R @@ -300,7 +300,6 @@ test_that("build_check_install works", { test_that("get_all_external_deps works", { - # dummy dependency_structure object deps <- list( @@ -368,12 +367,13 @@ test_that("get_all_external_deps works", { expect_equal(results, c("Q", "W", "U", "X", "V", "Y", "T", "Z", "S")) # message and unsorted list if not all of Depends, Imports and LinkingTo are `from_external_dependencies` - expect_message(results <- get_all_external_dependencies(x, - package_list = "B", - available_packages = available_packages, - from_external_dependencies = c("Depends", "Imports") - ), - regexp = "Packages will not be ordered as this requires" + expect_message( + results <- get_all_external_dependencies(x, + package_list = "B", + available_packages = available_packages, + from_external_dependencies = c("Depends", "Imports") + ), + regexp = "Packages will not be ordered as this requires" ) expect_equal(sort(results), c("T", "Z")) diff --git a/tests/testthat/test-dependencies_helper.R b/tests/testthat/test-dependencies_helper.R index e222bcd..0723bfa 100644 --- a/tests/testthat/test-dependencies_helper.R +++ b/tests/testthat/test-dependencies_helper.R @@ -137,7 +137,6 @@ test_that("yaml_from_dep_table works", { test_that("parse_remote_project works", { - # repo@host expect_equal( parse_remote_project("x@y"), @@ -229,7 +228,6 @@ test_that("run_package_actions works", { # parse_deps_table ---- test_that("parse_deps_table works as expected", { - # empty/NA returns character(0) expect_length(parse_deps_table(""), 0) expect_length(parse_deps_table(NA), 0) diff --git a/tests/testthat/test-utils.R b/tests/testthat/test-utils.R index 6c0503e..e53d4c6 100644 --- a/tests/testthat/test-utils.R +++ b/tests/testthat/test-utils.R @@ -82,3 +82,34 @@ test_that("rep_with_names works", { expect_equal(rep_with_names(list("hh"), c("aa", "bb")), list(aa = "hh", bb = "hh")) expect_equal(rep_with_names("hh", NULL), character(0)) }) + +test_that("setting verbosity works", { + verbose_sd_rm() + f <- function() { + return(verbose_sd_get()) + } + f2 <- function() { + return(f()) + } + expect_identical(f2(), 1) # Default + + # Assignment + lev <- 2 + verbose_sd_set(lev) + expect_identical(f2(), lev) + + # Removal + verbose_sd_rm() + expect_identical(f2(), 1) # Default + expect_error(verbose_sd_rm()) + + # Check that is only in the package environment + f3 <- function(lev) { + verbose_sd_set(lev) + ret <- f2() + verbose_sd_rm() + return(ret) + } + expect_identical(f3(lev), lev) + expect_identical(verbose_sd_get(), 1) +}) diff --git a/tests/testthat/test-zzz.R b/tests/testthat/test-zzz.R index 843f8b4..ead86ce 100644 --- a/tests/testthat/test-zzz.R +++ b/tests/testthat/test-zzz.R @@ -11,7 +11,12 @@ test_that("setup_storage_dir works", { # modify storage directory to a new random one new_storage_dir <- tempfile("random_storage_dir") set_storage_dir(new_storage_dir) - on.exit({setup_storage_dir(TESTS_STORAGE_DIR)}, add = TRUE) + on.exit( + { + setup_storage_dir(TESTS_STORAGE_DIR) + }, + add = TRUE + ) setup_storage_dir(new_storage_dir) expect_equal(get_storage_dir(), new_storage_dir)