Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add quarto_install_extension() / quarto_use_template() #45

Merged
merged 20 commits into from
Oct 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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)
})