diff --git a/NEWS.md b/NEWS.md index deb37af8..91b96ce7 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,8 @@ # rsconnect 0.8.30 (development version) +* `deployApp("foo.Rmd")` has been deprecated. It was never documented, and + it does the same job as `deployDoc()` (#698). + * `deployApp(appPrimaryDoc)` has been deprecated; it did the same job as `recordDir`. diff --git a/R/deployApp.R b/R/deployApp.R index 02bf9e0a..5d5af030 100644 --- a/R/deployApp.R +++ b/R/deployApp.R @@ -18,8 +18,8 @@ #' control to ensure that future you and other collaborators will publish #' to the same location. #' -#' @param appDir Directory containing application. Defaults to current working -#' directory. +#' @param appDir A directory containing an application (e.g. a Shiny app +#' or plumber API). Defaults to the current directory. #' @param appFiles A character vector given relative paths to the files and #' directories to bundle and deploy. The default, `NULL`, will include all #' files in `appDir`, apart from any listed in an `.rscignore` file. @@ -152,7 +152,29 @@ deployApp <- function(appDir = getwd(), condaMode <- FALSE + check_string(appDir) + if (isStaticFile(appDir) && !dirExists(appDir)) { + lifecycle::deprecate_warn( + when = "0.9.0", + what = "deployApp(appDir = 'takes a directory, not a document,')", + with = "deployDoc()" + ) + return(deployDoc( + appDir, + appName = appName, + appTitle = appTitle, + account = account, + server = server, + upload = upload, + recordDir = recordDir, + launch.browser = launch.browser, + logLevel = logLevel, + lint = lint + )) + } check_directory(appDir) + appDir <- normalizePath(appDir) + check_string(appName, allow_null = TRUE) if (!is.null(appPrimaryDoc)) { @@ -207,26 +229,6 @@ deployApp <- function(appDir = getwd(), on.exit(options(old_error), add = TRUE) } - # normalize appDir path - appDir <- normalizePath(appDir) - - # if a specific file is named, make sure it's an Rmd or HTML, and just deploy - # a single document in this case (this will call back to deployApp with a list - # of supporting documents) - rmdFile <- "" - if (!dirExists(appDir)) { - if (grepl("\\.[Rq]md$", appDir, ignore.case = TRUE) || - grepl("\\.html?$", appDir, ignore.case = TRUE)) { - return(deployDoc(appDir, appName = appName, appTitle = appTitle, - account = account, server = server, upload = upload, - recordDir = recordDir, launch.browser = launch.browser, - logLevel = logLevel, lint = lint)) - } else { - stop(appDir, " must be a directory, an R Markdown document, or an HTML ", - "document.") - } - } - # at verbose log level, generate header if (verbose) { logger("Deployment log started") diff --git a/R/deployDoc.R b/R/deployDoc.R index 4ac13df5..feea39f4 100644 --- a/R/deployDoc.R +++ b/R/deployDoc.R @@ -23,39 +23,51 @@ #' @family Deployment functions #' @export deployDoc <- function(doc, ...) { + doc <- standardizeSingleDocDeployment(doc) + deployApp( + appDir = doc$appDir, + appPrimaryDoc = doc$appPrimaryDoc, + appFiles = doc$appFiles, + ... + ) +} + +standardizeSingleDocDeployment <- function(path, + quiet = FALSE, + error_call = caller_env(), + error_arg = caller_arg(path)) { check_installed( "rmarkdown", version = "0.5.2", reason = "to deploy individual R Markdown documents" ) + check_file(path, error_call = error_call, error_arg = error_arg) + path <- normalizePath(path) - check_file(doc) - - # get qualified doc - qualified_doc <- normalizePath(doc, winslash = "/") + withStatus <- withStatus(quiet) - # see if this doc has runtime: shiny_prerendered, if it does then - # appFiles will be NULL (bundle the entire directory) - if (isShinyRmd(doc)) { - app_files <- NULL + if (isShinyRmd(path)) { + # deploy entire directory + appFiles <- NULL + } else if (isStaticFile(path)) { + # deploy file + dependenciy + withStatus("Discovering document dependencies", { + resources <- rmarkdown::find_external_resources(path) + }) + appFiles <- c(basename(path), resources$path) } else { - # default to deploying just the single file specified - app_files <- basename(qualified_doc) - - # if this document's type supports automated resource discovery, do that now, - # and add the discovered files to the deployment list - ext <- tolower(tools::file_ext(doc)) - if (ext %in% c("rmd", "qmd", "html", "htm")) { - message("Discovering document dependencies... ", appendLF = FALSE) - res <- rmarkdown::find_external_resources(qualified_doc) - message("OK") - app_files <- c(app_files, res$path) - } + # deploy just the file + appFiles <- basename(path) } - # deploy the document with the discovered dependencies - deployApp(appDir = dirname(qualified_doc), - appFiles = app_files, - appPrimaryDoc = basename(qualified_doc), - ...) + list( + appDir = normalizePath(dirname(path)), + appPrimaryDoc = basename(path), + appFiles = appFiles + ) +} + +isStaticFile <- function(path) { + ext <- tolower(tools::file_ext(path)) + ext %in% c("rmd", "qmd", "html", "htm") } diff --git a/man/deployApp.Rd b/man/deployApp.Rd index 47b7f699..9218e5a0 100644 --- a/man/deployApp.Rd +++ b/man/deployApp.Rd @@ -32,8 +32,8 @@ deployApp( ) } \arguments{ -\item{appDir}{Directory containing application. Defaults to current working -directory.} +\item{appDir}{A directory containing an application (e.g. a Shiny app +or plumber API). Defaults to the current directory.} \item{appFiles}{A character vector given relative paths to the files and directories to bundle and deploy. The default, \code{NULL}, will include all diff --git a/man/showMetrics.Rd b/man/showMetrics.Rd index a480f924..ad6a912f 100644 --- a/man/showMetrics.Rd +++ b/man/showMetrics.Rd @@ -25,8 +25,8 @@ for available series.} \href{https://docs.posit.co/shinyapps.io/metrics.html#ApplicationMetrics}{shinyapps.io documentation} for available metrics.} -\item{appDir}{Directory containing application. Defaults to current working -directory.} +\item{appDir}{A directory containing an application (e.g. a Shiny app +or plumber API). Defaults to the current directory.} \item{appName}{Name of application (names must be unique within an account). If not supplied on the first deploy, it will be generated from the base diff --git a/man/writeManifest.Rd b/man/writeManifest.Rd index 1b4fa01c..95fc8e0f 100644 --- a/man/writeManifest.Rd +++ b/man/writeManifest.Rd @@ -17,8 +17,8 @@ writeManifest( ) } \arguments{ -\item{appDir}{Directory containing application. Defaults to current working -directory.} +\item{appDir}{A directory containing an application (e.g. a Shiny app +or plumber API). Defaults to the current directory.} \item{appFiles}{A character vector given relative paths to the files and directories to bundle and deploy. The default, \code{NULL}, will include all diff --git a/tests/testthat/_snaps/deployApp.md b/tests/testthat/_snaps/deployApp.md index 4715b789..d0b1a263 100644 --- a/tests/testthat/_snaps/deployApp.md +++ b/tests/testthat/_snaps/deployApp.md @@ -1,3 +1,27 @@ +# appDir must be an existing directory + + Code + deployApp(1) + Condition + Error in `deployApp()`: + ! `appDir` must be a single string, not the number 1. + Code + deployApp("doesntexist") + Condition + Error in `deployApp()`: + ! `appDir`, "doesntexist", does not exist. + +# single document appDir is deprecated + + Code + deployApp("foo.Rmd") + Condition + Warning: + The `appDir` argument of `deployApp()` takes a directory, not a document, as of rsconnect 0.9.0. + i Please use `deployDoc()` instead. + Error in `deployDoc()`: + ! `doc`, "foo.Rmd", does not exist. + # appPrimaryDoc must exist, if supplied Code diff --git a/tests/testthat/_snaps/deployDoc.md b/tests/testthat/_snaps/deployDoc.md new file mode 100644 index 00000000..6f76bb70 --- /dev/null +++ b/tests/testthat/_snaps/deployDoc.md @@ -0,0 +1,8 @@ +# deployDoc correctly reports bad path + + Code + deployDoc("doesntexist.Rmd") + Condition + Error in `deployDoc()`: + ! `doc`, "doesntexist.Rmd", does not exist. + diff --git a/tests/testthat/test-deployApp.R b/tests/testthat/test-deployApp.R index d4ee668c..b3d234bf 100644 --- a/tests/testthat/test-deployApp.R +++ b/tests/testthat/test-deployApp.R @@ -1,3 +1,16 @@ +test_that("appDir must be an existing directory", { + expect_snapshot(error = TRUE, { + deployApp(1) + deployApp("doesntexist") + }) +}) + +test_that("single document appDir is deprecated", { + expect_snapshot(error = TRUE, { + deployApp("foo.Rmd") + }) +}) + test_that("appPrimaryDoc must exist, if supplied", { dir <- local_temp_app() diff --git a/tests/testthat/test-deployDoc.R b/tests/testthat/test-deployDoc.R new file mode 100644 index 00000000..6d071152 --- /dev/null +++ b/tests/testthat/test-deployDoc.R @@ -0,0 +1,43 @@ +test_that("deployDoc correctly reports bad path", { + expect_snapshot(deployDoc("doesntexist.Rmd"), error = TRUE) +}) + +# standardizeSingleDocDeployment ------------------------------------------ + +test_that("turns appDir into appDir + appPrimarySourceDoc", { + dir <- local_temp_app(list("foo.R" = "")) + + doc <- standardizeSingleDocDeployment(file.path(dir, "foo.R")) + expect_equal(doc$appDir, normalizePath(dir)) + expect_equal(doc$appPrimaryDoc, "foo.R") +}) + +test_that("shiny rmd deploys whole directory", { + dir <- local_temp_app(list("foo.Rmd" = c( + "---", + "runtime: shiny", + "---" + ))) + doc <- standardizeSingleDocDeployment(file.path(dir, "foo.Rmd")) + expect_equal(doc$appFiles, NULL) +}) + +test_that("regular rmd deploys file and dependencies", { + dir <- local_temp_app(list( + "foo.Rmd" = c( + "---", + "resource_files: [foo.csv]", + "---" + ), + "foo.csv" = "" + )) + + doc <- standardizeSingleDocDeployment(file.path(dir, "foo.Rmd"), quiet = TRUE) + expect_equal(doc$appFiles, c("foo.Rmd", "foo.csv")) +}) + +test_that("other types deploy that one file", { + dir <- local_temp_app(list("foo.R" = "")) + doc <- standardizeSingleDocDeployment(file.path(dir, "foo.R")) + expect_equal(doc$appFiles, "foo.R") +})