Skip to content

Commit

Permalink
Merge pull request #45 from mcanouil/feat-new-commands
Browse files Browse the repository at this point in the history
Co-authored-by: Christophe Dervieux <christophe.dervieux@gmail.com>
  • Loading branch information
cderv authored Oct 13, 2023
2 parents 2fd494e + ab0af75 commit 1a4ba6c
Show file tree
Hide file tree
Showing 17 changed files with 273 additions and 9 deletions.
4 changes: 3 additions & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Package: quarto
Title: R Interface to 'Quarto' Markdown Publishing System
Version: 1.3.3
Version: 1.3.4
Authors@R: c(
person("JJ", "Allaire", , "jj@rstudio.com", role = c("aut", "cre"),
comment = c(ORCID = "0000-0003-0174-9868")),
Expand All @@ -14,9 +14,11 @@ URL: https://github.com/quarto-dev/quarto-r,
https://quarto-dev.github.io/quarto-r/
BugReports: https://github.com/quarto-dev/quarto-r/issues
Imports:
cli,
jsonlite,
later,
processx,
rlang,
rmarkdown,
rsconnect (>= 0.8.26),
rstudioapi,
Expand Down
7 changes: 7 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Generated by roxygen2: do not edit by hand

export(quarto_add_extension)
export(quarto_inspect)
export(quarto_path)
export(quarto_preview)
Expand All @@ -9,10 +10,16 @@ export(quarto_publish_doc)
export(quarto_publish_site)
export(quarto_render)
export(quarto_serve)
export(quarto_use_template)
export(quarto_version)
import(rlang)
importFrom(cli,cli_abort)
importFrom(cli,cli_inform)
importFrom(jsonlite,fromJSON)
importFrom(later,later)
importFrom(processx,process)
importFrom(processx,run)
importFrom(rlang,is_interactive)
importFrom(rmarkdown,relative_to)
importFrom(rstudioapi,isAvailable)
importFrom(rstudioapi,viewer)
Expand Down
2 changes: 2 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# quarto (development version)

* Add `quarto_add_extension()` and `quarto_use_template()` to deal with Quarto extensions for a Quarto project. (thanks, @mcanouil, #45, @remlapmot, #42).

* Add `profile` arguments to `quarto_render()` and `quarto_inspect()` (thanks, #95, @andrewheiss, #123, @salim-b).

* Add `metadata` and `metadata_file` to `quarto_render()` to pass modify Quarto metadata from calling render. If both are set, `metadata` will be merged over `metadata_file` content. Internally, metadata will be passed as a `--metadata-file` to `quarto render` (thanks, @mcanouil, #52, @maelle, #49).
Expand Down
3 changes: 3 additions & 0 deletions R/aaa.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#' Internal package state
#' @noRd
quarto <- new.env(parent = emptyenv())
49 changes: 49 additions & 0 deletions R/add.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#' Install a Quarto extensions
#'
#' Add an extension to this folder or project by running `quarto add`
#'
#' # Extension Trust
#'
#' Quarto extensions may execute code when documents are rendered. Therefore, if
#' you do not trust the author of an extension, we recommend that you do not
#' install or use the extension.
#' By default `no_prompt = FALSE` which means that
#' the function will ask for explicit approval when used interactively, or
#' disallow installation.
#'
#' @param extension The extension to install, either an archive or a GitHub
#' repository as described in the documentation
#' <https://quarto.org/docs/extensions/managing.html>.
#'
#' @param no_prompt Do not prompt to confirm approval to download external extension.
#'
#' @examples
#' \dontrun{
#' # Install a template and set up a draft document from a GitHub repository
#' quarto_add_extension("quarto-ext/fontawesome")
#'
#' # Install a template and set up a draft document from a ZIP archive
#' quarto_add_extension("https://github.com/quarto-ext/fontawesome/archive/refs/heads/main.zip")
#' }
#'
#' @importFrom rlang is_interactive
#' @importFrom cli cli_abort
#' @export
quarto_add_extension <- function(extension = NULL, no_prompt = FALSE) {
rlang::check_required(extension)

quarto_bin <- find_quarto()

# This will ask for approval or stop installation
check_extension_approval(no_prompt, "Quarto extensions", "https://quarto.org/docs/extensions/managing.html")

args <- c(extension,"--no-prompt")

quarto_add(args, quarto_bin = quarto_bin, echo = TRUE)

invisible()
}

quarto_add <- function(args = character(), ...) {
quarto_run_what("add", args = args, ...)
}
2 changes: 1 addition & 1 deletion R/inspect.R
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ quarto_inspect <- function(input = ".",
args <- c(args, c("--profile", paste0(profile, collapse = ",")))
}

res <- processx::run(quarto_bin, args, echo_cmd = getOption("quarto.echo_cmd", FALSE))
res <- quarto_run(args, quarto_bin = quarto_bin)

fromJSON(res$stdout)
}
10 changes: 7 additions & 3 deletions R/quarto-package.R
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
#' @keywords internal
"_PACKAGE"

#' Internal package state
#' @noRd
quarto <- new.env(parent = emptyenv())
## usethis namespace: start
#' @import rlang
#' @importFrom cli cli_inform
## usethis namespace: end
NULL
15 changes: 15 additions & 0 deletions R/quarto.R
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,18 @@ quarto_version <- function() {
quarto_bin <- find_quarto()
as.numeric_version(system2(quarto_bin, "--version", stdout = TRUE))
}

#' @importFrom processx run
quarto_run <- function(args = character(), quarto_bin = find_quarto(), echo = FALSE, echo_cmd = getOption("quarto.echo_cmd", FALSE), ..., .call = rlang::caller_env()) {
res <- tryCatch({
processx::run(quarto_bin, args = args, echo = FALSE, error_on_status = TRUE, echo_cmd = echo_cmd, ...)
},
error = function(e) rlang::abort(c("Error running quarto cli:", x = e$stderr), call = .call, parent = e)
)
invisible(res)
}

quarto_run_what <- function(what = character(), args = character(), quarto_bin = find_quarto(), echo = FALSE, ..., .call = rlang::caller_env()) {
res <- quarto_run(quarto_bin, args = c(what, args), echo = FALSE, ..., .call = .call)
invisible(res)
}
8 changes: 4 additions & 4 deletions R/render.R
Original file line number Diff line number Diff line change
Expand Up @@ -83,15 +83,15 @@ quarto_render <- function(input = NULL,
pandoc_args = NULL,
as_job = getOption("quarto.render_as_job", "auto")) {

# get quarto binary
quarto_bin <- find_quarto()

# provide default for input
if (is.null(input)) {
input <- getwd()
}
input <- path.expand(input)

# get quarto binary
quarto_bin <- find_quarto()

# see if we need to render as a job
if (identical(as_job, "auto")) {
as_job <- utils::file_test("-d", input)
Expand Down Expand Up @@ -179,7 +179,7 @@ quarto_render <- function(input = NULL,
}

# run quarto
processx::run(quarto_bin, args, echo = TRUE)
quarto_run(args, echo = TRUE, quarto_bin = quarto_bin)

# no return value
invisible(NULL)
Expand Down
38 changes: 38 additions & 0 deletions R/use.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#' Use a custom format extension template
#'
#' Install and use a template for Quarto using `quarto use`.
#'
#' @inheritParams quarto_add_extension
#'
#' @param template The template to install, either an archive or a GitHub
#' repository as described in the documentation
#' <https://quarto.org/docs/extensions/formats.html>.
#'
#' @examples
#' \dontrun{
#' # Install a template and set up a draft document from a GitHub repository
#' quarto_use_template("quarto-journals/jss")
#'
#' # Install a template and set up a draft document from a ZIP archive
#' quarto_use_template("https://github.com/quarto-journals/jss/archive/refs/heads/main.zip")
#' }
#'
#' @export
quarto_use_template <- function(template, no_prompt = FALSE) {
rlang::check_required(template)

quarto_bin <- find_quarto()

# This will ask for approval or stop installation
check_extension_approval(no_prompt, "Quarto templates", "https://quarto.org/docs/extensions/formats.html#distributing-formats")

args <- c("template", template, "--no-prompt")

quarto_use(args, quarto_bin = quarto_bin, echo = TRUE)

invisible()
}

quarto_use <- function(args = character(), ...) {
quarto_run_what("use", args = args, ...)
}
23 changes: 23 additions & 0 deletions R/utils-prompt.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
check_extension_approval <- function(no_prompt = FALSE, what = "Something", see_more_at = NULL) {
if (!no_prompt) {
if (!rlang::is_interactive()) {
cli::cli_abort(c(
"{ what } requires explicit approval.",
'>' = "Set {.arg no_prompt = TRUE} if you agree.",
if (!is.null(see_more_at)) {
c(i = "See more at {.url {see_more_at}}")
}
))
} else {
cli::cli_inform(c(
"{what} may execute code when documents are rendered. ",
'*' = "If you do not trust the author(s) of this {what}, we recommend that you do not install or use this {what}."
))
prompt_value <- tolower(readline(sprintf("Do you trust the authors of this %s (Y/n)? ", what)))
if (!prompt_value %in% "y") {
cli::cli_inform("{what} not installed.")
return(invisible(FALSE))
}
}
}
}
8 changes: 8 additions & 0 deletions _pkgdown.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,14 @@ reference:
contents:
- quarto_publish_doc

- title: "Extensions"
desc: >
This functions enable you to install extensions and use template from extensions in your folder and projects.
More about Quarto extensions at <https://quarto.org/docs/extensions/>
contents:
- starts_with("quarto_add")
- starts_with("quarto_use")

- title: "Configuration"
desc: >
These functions enable you to inspect the Quarto installation as well as the metadata
Expand Down
29 changes: 29 additions & 0 deletions man/quarto-package.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

37 changes: 37 additions & 0 deletions man/quarto_add_extension.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

28 changes: 28 additions & 0 deletions man/quarto_use_template.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions tests/testthat/test-add.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
test_that("Installing an extension", {
skip_if_no_quarto()
skip_if_offline("github.com")
qmd <- local_qmd_file(c("content"))
withr::local_dir(dirname(qmd))
expect_error(quarto_add_extension("quarto-ext/fontawesome"), "explicit approval")
quarto_add_extension("quarto-ext/fontawesome", no_prompt = TRUE)
expect_true(dir.exists("_extensions/quarto-ext/fontawesome"))
})
10 changes: 10 additions & 0 deletions tests/testthat/test-use.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
test_that("Installing an extension", {
skip_if_no_quarto()
skip_if_offline("github.com")
dir <- withr::local_tempdir()
withr::local_dir(dir)
expect_error(quarto_use_template("quarto-journals/jss"), "explicit approval")
quarto_use_template("quarto-journals/jss", no_prompt = TRUE)
expect_true(dir.exists("_extensions/quarto-journals/jss"))
expect_length(list.files(pattern = "[.]qmd$"), 1)
})

0 comments on commit 1a4ba6c

Please sign in to comment.