From a4d66ce004c40e4468822f01fc39a6b8087a792a Mon Sep 17 00:00:00 2001 From: Christophe Dervieux Date: Thu, 7 Nov 2024 12:45:16 +0100 Subject: [PATCH] Add QUARTO_KNITR_RSCRIPT_ARGS environment variable to pass some flags to Rscript run by Quarto for knitr engine. This can be useful in specific situation like e.g `--max-connections=258` available in R 4.4, or `--vanilla` Args should be passed a comma separated list of flags. This is for expert use and there is not check done on the argument being support by RScript or not. --- src/execute/rmd.ts | 8 ++++++ tests/docs/knitr/rscript-args.qmd | 12 +++++++++ tests/smoke/knitr/rscript-args.test.ts | 31 ++++++++++++++++++++++ tests/smoke/render/render-required.test.ts | 11 +++----- tests/utils.ts | 16 ++++++++++- 5 files changed, 69 insertions(+), 9 deletions(-) create mode 100644 tests/docs/knitr/rscript-args.qmd create mode 100644 tests/smoke/knitr/rscript-args.test.ts diff --git a/src/execute/rmd.ts b/src/execute/rmd.ts index 3e44f8e7387..97b747a5b0d 100644 --- a/src/execute/rmd.ts +++ b/src/execute/rmd.ts @@ -272,11 +272,19 @@ async function callR( wd: cwd, }); + // QUARTO_KNITR_RSCRIPT_ARGS allows to pass additional arguments to Rscript as comma separated values + // e.g. QUARTO_KNITR_RSCRIPT_ARGS="--vanilla,--no-init-file,--max-connections=258" + const rscriptArgs = Deno.env.get("QUARTO_KNITR_RSCRIPT_ARGS") || ""; + const rscriptArgsArray = rscriptArgs.split(",").filter((a) => + a.trim() !== "" + ); + try { const result = await execProcess( { cmd: [ await rBinaryPath("Rscript"), + ...rscriptArgsArray, resourcePath("rmd/rmd.R"), ], cwd, diff --git a/tests/docs/knitr/rscript-args.qmd b/tests/docs/knitr/rscript-args.qmd new file mode 100644 index 00000000000..6212240e6d9 --- /dev/null +++ b/tests/docs/knitr/rscript-args.qmd @@ -0,0 +1,12 @@ +--- +title: Rscript args passed to quarto +format: markdown_strict +--- + +```{r} +#| echo: false +args <- commandArgs(trailingOnly = FALSE) +# keep only flags +args <- args[grep("^--", args)] +args +``` diff --git a/tests/smoke/knitr/rscript-args.test.ts b/tests/smoke/knitr/rscript-args.test.ts new file mode 100644 index 00000000000..d92fd2a31ff --- /dev/null +++ b/tests/smoke/knitr/rscript-args.test.ts @@ -0,0 +1,31 @@ +/* + * cache.test.ts + * + * Copyright (C) 2023 Posit Software, PBC + */ +import { fileLoader, setEnvVar, restoreEnvVar } from "../../utils.ts"; +import { testRender } from "../render/render.ts"; +import { ensureFileRegexMatches, noErrorsOrWarnings } from "../../verify.ts"; + +// Default case should not error +const rscriptArgsDoc = fileLoader()("knitr/rscript-args.qmd", "markdown_strict"); +testRender(rscriptArgsDoc.input, "markdown_strict", true, [ + noErrorsOrWarnings, +]); + +// Set special flag through env var +let rscriptArgs: string | undefined; +testRender(rscriptArgsDoc.input, "markdown_strict", true, [ + noErrorsOrWarnings, + ensureFileRegexMatches(rscriptArgsDoc.output.outputPath, [ + /"--vanilla"/, /"--max-connection=258"/] + ), +], +{ + setup: async () => { + rscriptArgs = setEnvVar("QUARTO_KNITR_RSCRIPT_ARGS", "--vanilla,--max-connections=258"); + }, + teardown: async () => { + restoreEnvVar("QUARTO_KNITR_RSCRIPT_ARGS", rscriptArgs); + }, +}); \ No newline at end of file diff --git a/tests/smoke/render/render-required.test.ts b/tests/smoke/render/render-required.test.ts index 4cb83d36bb3..ddc371c8bef 100644 --- a/tests/smoke/render/render-required.test.ts +++ b/tests/smoke/render/render-required.test.ts @@ -6,7 +6,7 @@ */ import { testQuartoCmd } from "../../test.ts"; -import { docs } from "../../utils.ts"; +import { docs, setEnvVar, restoreEnvVar } from "../../utils.ts"; import { printsMessage } from "../../verify.ts"; const input = docs("quarto-required.qmd"); @@ -27,8 +27,7 @@ testQuartoCmd( { setup: async () => { // Save current version of QUARTO_VERSION_REQUIREMENT env var and set it to a value that will not be satisfied - oldVersionRequirement = Deno.env.get("QUARTO_VERSION_REQUIREMENT"); - Deno.env.set("QUARTO_VERSION_REQUIREMENT", "< 0.0.0"); + oldVersionRequirement = setEnvVar("QUARTO_VERSION_REQUIREMENT", "< 0.0.0"); // Mock Deno.exit to throw an error instead of exiting // Otherwise we would not check the error assertion originalDenoExit = Deno.exit; @@ -38,11 +37,7 @@ testQuartoCmd( }, teardown: async () => { // Restore QUARTO_VERSION_REQUIREMENT - if (oldVersionRequirement) { - Deno.env.set("QUARTO_VERSION_REQUIREMENT", oldVersionRequirement); - } else { - Deno.env.delete("QUARTO_VERSION_REQUIREMENT"); - } + restoreEnvVar("QUARTO_VERSION_REQUIREMENT", oldVersionRequirement); // Restore Deno.exit Deno.exit = originalDenoExit; }, diff --git a/tests/utils.ts b/tests/utils.ts index 7155f6195ea..665f9bce2b0 100644 --- a/tests/utils.ts +++ b/tests/utils.ts @@ -114,7 +114,7 @@ export function outputForInput( if (baseFormat === "revealjs") { outputExt = "html"; } - if (["commonmark", "gfm", "markdown"].some((f) => f === baseFormat)) { + if (["commonmark", "gfm", "markdown", "markdown_strict"].some((f) => f === baseFormat)) { outputExt = "md"; } if (baseFormat === "csljson") { @@ -190,3 +190,17 @@ export function fileLoader(...path: string[]) { export function quartoDevCmd(): string { return Deno.build.os === "windows" ? "quarto.cmd" : "quarto"; } + +export function setEnvVar(name: string, value: string): string | undefined { + const originalValue = Deno.env.get(name); + Deno.env.set(name, value); + return originalValue; +} + +export function restoreEnvVar(name: string, originalValue: string | undefined): void { + if (originalValue !== undefined) { + Deno.env.set(name, originalValue); + } else { + Deno.env.delete(name); + } +}