From 334971a639dfd1e24a68dc1aad9cf76b67d7dbbe Mon Sep 17 00:00:00 2001 From: "Jennifer (Jenny) Bryan" Date: Tue, 14 Jun 2022 07:28:48 -0700 Subject: [PATCH] Rationalize nested projects (#1647) * Rationalize nested projects Closes #1643 We discourage nested projects, but it is possible, mostly for developers. So we should at least behave correctly in this code path. Previously, a nested project would put the name of the parent project in DESCRIPTION, which causes a variety of downstream problems. * NEWS bullet * Use an undocumented option to bypass the interactive challenge Useful in tests (replaces the mock) and also to a developer, like @lionel-, who might need to do this often enough to set the option. --- NEWS.md | 2 ++ R/create.R | 12 +++++------ R/description.R | 4 ++++ R/proj.R | 2 +- tests/testthat/test-create.R | 39 +++++++++++++++++++----------------- 5 files changed, 33 insertions(+), 26 deletions(-) diff --git a/NEWS.md b/NEWS.md index f0ddf60e8..895d18862 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,7 @@ # usethis (development version) +Although nested projects are discouraged, they can be useful in development contexts. `create_package()` now sets the correct package name and returns the correct package path for a package nested inside a project (#1647). + `git_vaccinated()` now treats a path configured as `core.excludesFile` like other user-supplied paths; in particular, any use of the `~/` home directory shortcut is expanded via [`fs::path_expand()`](https://fs.r-lib.org/reference/path_expand.html) (@dpprdan, #1560). # usethis 2.1.6 diff --git a/R/create.R b/R/create.R index bec968145..ba4310ed5 100644 --- a/R/create.R +++ b/R/create.R @@ -51,7 +51,7 @@ create_package <- function(path, local_project(path, force = TRUE) use_directory("R") - use_description(fields, check_name = FALSE, roxygen = roxygen) + use_description_impl_(name, fields, roxygen) use_namespace(roxygen = roxygen) if (rstudio) { @@ -307,17 +307,15 @@ create_from_github <- function(repo_spec, invisible(proj_get()) } -# creates a backdoor we can exploit in tests -allow_nested_project <- function() FALSE - challenge_nested_project <- function(path, name) { if (!possibly_in_proj(path)) { return(invisible()) } - # we mock this in a few tests, to allow a nested project - if (allow_nested_project()) { - return() + # creates an undocumented backdoor we can exploit when the interactive + # approval is impractical, e.g. in tests + if (isTRUE(getOption("usethis.allow_nested_package", FALSE))) { + return(invisible()) } ui_line( diff --git a/R/description.R b/R/description.R index df86125c0..e8b8411cb 100644 --- a/R/description.R +++ b/R/description.R @@ -65,6 +65,10 @@ use_description <- function(fields = list(), check_package_name(name) } + use_description_impl_(name = name, fields = fields, roxygen = roxygen) +} + +use_description_impl_ <- function(name, fields = list(), roxygen = TRUE) { desc <- build_description(name, roxygen = roxygen, fields = fields) tf <- withr::local_tempfile(pattern = glue("use_description-{name}-")) diff --git a/R/proj.R b/R/proj.R index 8b108b2b3..9535ed413 100644 --- a/R/proj.R +++ b/R/proj.R @@ -69,7 +69,7 @@ proj_get <- function() { #' adding a `DESCRIPTION` file. #' @export proj_set <- function(path = ".", force = FALSE) { - if (dir_exists(path %||% "") && is_in_proj(path)) { + if (!force && dir_exists(path %||% "") && is_in_proj(path)) { return(invisible(proj_get_())) } diff --git a/tests/testthat/test-create.R b/tests/testthat/test-create.R index 190ebc210..a5a19358e 100644 --- a/tests/testthat/test-create.R +++ b/tests/testthat/test-create.R @@ -37,28 +37,31 @@ test_that("nested project is disallowed, by default", { test_that("nested package can be created if user really, really wants to", { parent <- create_local_package() - with_mock( - # since user can't approve interactively, use the backdoor - allow_nested_project = function() TRUE, - { - child <- create_package(path(parent, "fghijk")) - } - ) - expect_true(possibly_in_proj(child)) - expect_true(is_package(child)) + child_path <- path(parent, "fghijk") + + # since user can't approve interactively, use the backdoor + withr::local_options("usethis.allow_nested_package" = TRUE) + + child_result <- create_package(child_path) + + expect_equal(child_path, child_result) + expect_true(possibly_in_proj(child_path)) + expect_true(is_package(child_path)) + expect_equal(project_name(child_path), "fghijk") }) test_that("nested project can be created if user really, really wants to", { parent <- create_local_project() - with_mock( - # since user can't approve interactively, use the backdoor - allow_nested_project = function() TRUE, - { - child <- create_project(path(parent, "fghijk")) - } - ) - expect_true(possibly_in_proj(child)) - expect_false(is_package(child)) + child_path <- path(parent, "fghijk") + + # since user can't approve interactively, use the backdoor + withr::local_options("usethis.allow_nested_package" = TRUE) + + child_result <- create_project(child_path) + + expect_equal(child_path, child_result) + expect_true(possibly_in_proj(child_path)) + expect_equal(project_name(child_path), "fghijk") }) test_that("can create package in current directory (literally in '.')", {