diff --git a/DESCRIPTION b/DESCRIPTION index b57a41c6..ee616d9d 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: quarto Title: R Interface to 'Quarto' Markdown Publishing System -Version: 1.4.4.9004 +Version: 1.4.4.9005 Authors@R: c( person("JJ", "Allaire", , "jj@posit.co", role = "aut", comment = c(ORCID = "0000-0003-0174-9868")), diff --git a/NEWS.md b/NEWS.md index 00de6199..32093766 100644 --- a/NEWS.md +++ b/NEWS.md @@ -12,6 +12,8 @@ - `QUARTO_R_QUIET` environment variable can be used to set `quarto.quiet` option, which overrides any `quiet = TRUE` argument passed to `quarto_*` functions. This can be useful to debug Quarto rendering inside other packages, like **pkgdown**. Overrides will also now happens for [GHA debug logging](https://docs.github.com/en/actions/monitoring-and-troubleshooting-workflows/troubleshooting-workflows/enabling-debug-logging). +- Correctly report Quarto CLI error when background process call to `quarto` fails (thanks, @salim-b, [#235](https://github.com/quarto-dev/quarto-r/issues/235)) + # quarto 1.4.4 - `quarto_preview()` now looks at `quarto preview` log to browse to the correct url when inside RStudio viewer (thanks, @aronatkins, #167). diff --git a/R/quarto.R b/R/quarto.R index 529741ed..0100e64f 100644 --- a/R/quarto.R +++ b/R/quarto.R @@ -3,10 +3,12 @@ #' Determine the path to the quarto binary. Uses `QUARTO_PATH` environment #' variable if defined, otherwise uses `Sys.which()`. #' +#' @param normalize If `TRUE` (default), normalize the path using [base::normalizePath()]. +#' #' @return Path to quarto binary (or `NULL` if not found) #' #' @export -quarto_path <- function() { +quarto_path <- function(normalize = TRUE) { path_env <- get_quarto_path_env() quarto_path <- if (is.na(path_env)) { path <- unname(Sys.which("quarto")) @@ -14,6 +16,9 @@ quarto_path <- function() { } else { path_env } + if (!normalize) { + return(quarto_path) + } normalizePath(quarto_path, winslash = "/", mustWork = FALSE) } @@ -69,11 +74,25 @@ quarto_run <- function( }, error = function(e) { msg <- c(x = "Error running quarto cli.") + # if there is an error message from quarto CLI, add it to the message + if (e$stderr != "") { + quarto_error_msg <- xfun::split_lines(e$stderr) + names(quarto_error_msg) <- rep(" ", length(quarto_error_msg)) + msg <- c( + msg, + " " = paste0(rep("-", nchar(msg)), collapse = ""), + quarto_error_msg + ) + } + + # if `--quiet` has been set, quarto CLI won't report any error (e$stderr will be empty) + # So remind user to run without `--quiet` to see the full error message if (cli_arg_quiet() %in% args) msg <- c( msg, "i" = "Rerun with `quiet = FALSE` to see the full error message." ) + cli::cli_abort(msg, call = .call, parent = e) } ) diff --git a/man/quarto_path.Rd b/man/quarto_path.Rd index 43a0e446..bf1cf3c7 100644 --- a/man/quarto_path.Rd +++ b/man/quarto_path.Rd @@ -4,7 +4,10 @@ \alias{quarto_path} \title{Path to the quarto binary} \usage{ -quarto_path() +quarto_path(normalize = TRUE) +} +\arguments{ +\item{normalize}{If \code{TRUE} (default), normalize the path using \code{\link[base:normalizePath]{base::normalizePath()}}.} } \value{ Path to quarto binary (or \code{NULL} if not found) diff --git a/tests/testthat/_snaps/quarto.md b/tests/testthat/_snaps/quarto.md index 97d1075d..d5aac1ed 100644 --- a/tests/testthat/_snaps/quarto.md +++ b/tests/testthat/_snaps/quarto.md @@ -9,6 +9,31 @@ Caused by error: ! System command 'quarto' failed +# quarto_run report full quarto cli error message + + Code + quarto_inspect() + Condition + Error in `quarto_inspect()`: + x Error running quarto cli. + ------------------------- + ERROR: Book chapter 'intro.qmd' not found + + Stack trace: + at throwInputNotFound () + at findInputs () + at eventLoopTick (ext:core/01_core.js:175:7) + at async findChapters () + at async bookRenderItems () + at async Object.bookProjectConfig [as config] () + at async projectContext () + at async inspectConfig () + at async Command.actionHandler () + at async Command.execute () + + Caused by error: + ! System command 'quarto' failed + # is_using_quarto correctly check directory Code diff --git a/tests/testthat/helper.R b/tests/testthat/helper.R index 8d4087d6..7272f95c 100644 --- a/tests/testthat/helper.R +++ b/tests/testthat/helper.R @@ -63,6 +63,34 @@ local_qmd_file <- function(..., .env = parent.frame()) { path } +local_quarto_project <- function( + name = "test-project", + type, + ..., + .env = parent.frame() +) { + skip_if_no_quarto() + path_tmp <- withr::local_tempdir( + pattern = "quarto-tests-project-", + .local_envir = .env + ) + tryCatch( + quarto_create_project( + name = name, + type = type, + dir = path_tmp, + no_prompt = TRUE, + quiet = TRUE, + ... + ), + error = function(e) { + stop("Creating temp project for tests failed", call. = FALSE) + } + ) + # return the path to the created project + return(file.path(path_tmp, name)) +} + .render <- function(input, output_file = NULL, ..., .env = parent.frame()) { skip_if_no_quarto() skip_if_not_installed("withr") @@ -105,28 +133,66 @@ expect_snapshot_qmd_output <- function(name, input, output_file = NULL, ...) { transform_quarto_cli_in_output <- function( full_path = FALSE, - normalize_path = FALSE, - version = FALSE + version = FALSE, + dir_only = FALSE ) { + hide_path <- function(lines, real_path) { + gsub( + real_path, + "", + lines, + fixed = TRUE + ) + } + return( function(lines) { if (full_path) { quarto_found <- find_quarto() - if (normalize_path) { - quarto_found <- normalizePath(quarto_found, mustWork = FALSE) + if (dir_only) { + quarto_found <- dirname(quarto_found) } - lines <- gsub(quarto_found, "", lines, fixed = TRUE) + quarto_found_normalized <- normalizePath(quarto_found, mustWork = FALSE) + # look for non-normalized path + lines <- hide_path(lines, quarto_found) + # look for normalized path + lines <- hide_path(lines, quarto_found_normalized) + + non_normalized_path <- quarto_path(normalize = FALSE) + non_normalized_path_slash <- gsub("\\\\", "/", non_normalized_path) + lines <- hide_path(lines, non_normalized_path) + lines <- hide_path(lines, non_normalized_path_slash) + # seems like there are quotes around path in CI windows lines <- gsub( - "\"\"", - "", - lines, - fixed = TRUE + "\"([^\"]*)\"", + "\\1", + lines ) + + # Handle quarto.js in stackstrace outputs + lines <- gsub( + "file:[/]{2,3}[/\\]quarto.js:\\d+:\\d+", + "", + lines + ) + # fixup binary name difference it exists in the output + # windows is quarto.exe while quarto on other OS + lines <- gsub("quarto.exe", "quarto", lines, fixed = TRUE) } else { # it will be quarto.exe only on windows lines <- gsub("quarto\\.(exe|cmd)", "quarto", lines) } + + # fallback: Above can fail on some windows situation, so try a regex match + # it should only match windows path with Drive letters + lines <- gsub( + "file:[/]{2,3}[A-Za-z]:[\\\\/](?:[^:\\n]+[\\\\/])*bin[\\\\/]quarto\\.js:\\d+:\\d+", + "", + lines, + perl = TRUE + ) + if (version) { lines <- gsub(quarto_version(), "", lines, fixed = TRUE) } diff --git a/tests/testthat/project/.gitignore b/tests/testthat/project/.gitignore new file mode 100644 index 00000000..4acd1968 --- /dev/null +++ b/tests/testthat/project/.gitignore @@ -0,0 +1 @@ +/.quarto \ No newline at end of file diff --git a/tests/testthat/test-quarto.R b/tests/testthat/test-quarto.R index d75380e8..f98da147 100644 --- a/tests/testthat/test-quarto.R +++ b/tests/testthat/test-quarto.R @@ -14,6 +14,26 @@ test_that("quarto_run gives guidance in error", { ) }) +test_that("quarto_run report full quarto cli error message", { + skip_if_no_quarto() + local_reproducible_output(width = 1000) + # Ensure we don't have colors in the output for quarto-cli error + withr::local_envvar(list(NO_COLOR = 1L)) + # https://github.com/quarto-dev/quarto-r/issues/235 + tmp_proj <- local_quarto_project(type = "book") + withr::local_dir(tmp_proj) + # simulate an error by renaming the intro.qmd + file.rename(from = "intro.qmd", to = "no_intro.qmd") + expect_snapshot( + error = TRUE, + quarto_inspect(), + transform = transform_quarto_cli_in_output( + full_path = TRUE, + dir_only = TRUE + ) + ) +}) + test_that("is_using_quarto correctly check directory", { qmd <- local_qmd_file(c("content")) # Only qmd @@ -63,7 +83,7 @@ test_that("quarto CLI sitrep", { lines, fixed = TRUE ) - transform_quarto_cli_in_output(full_path = TRUE, normalize_path = TRUE)( + transform_quarto_cli_in_output(full_path = TRUE)( lines ) } @@ -75,8 +95,7 @@ test_that("quarto CLI sitrep", { expect_snapshot( quarto_binary_sitrep(debug = TRUE), transform = transform_quarto_cli_in_output( - full_path = TRUE, - normalize_path = TRUE + full_path = TRUE ) ) )