From d0009296956d7eff0388cea6ffe2eef7de2debe6 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 26 Jul 2026 22:01:04 +0000 Subject: [PATCH 1/2] Await which() in dependency configure guards The non-vendored branch of each dependency's configure callback verifies the binary is on PATH with `if (which(file) === undefined)`. `which` is async, so this compares a Promise to undefined: always false, guard never fires. Effect: with QUARTO_VENDOR_BINARIES=false, configureDependency skips the download but still calls configure(). The guard is the only thing meant to catch a missing binary, so the build reports success and produces a Quarto tree without it. For typst-gather this surfaces only at render time, as "typst-gather analyze failed; staging all packages as fallback". Fixes the same bug in typst-gather, typst, pandoc and esbuild. Also refresh the "binary not found" message in typstGatherBinaryPath() and the dev-call command: configure.sh no longer builds typst-gather (the cargo step and package/typst-gather/ are gone; it is a downloaded release binary), and neither message mentioned QUARTO_TYPST_GATHER, which both already honour. Co-Authored-By: Claude Opus 5 --- package/src/common/dependencies/esbuild.ts | 2 +- package/src/common/dependencies/pandoc.ts | 2 +- package/src/common/dependencies/typst-gather.ts | 2 +- package/src/common/dependencies/typst.ts | 2 +- src/command/dev-call/typst-gather/cmd.ts | 15 +++++++++++---- src/core/typst-gather.ts | 8 ++++++-- 6 files changed, 21 insertions(+), 10 deletions(-) diff --git a/package/src/common/dependencies/esbuild.ts b/package/src/common/dependencies/esbuild.ts index 335e90050b0..cbdfd9266c6 100644 --- a/package/src/common/dependencies/esbuild.ts +++ b/package/src/common/dependencies/esbuild.ts @@ -56,7 +56,7 @@ export function esBuild(version: string): Dependency { } } else { // verify that the binary is on PATH, but otherwise don't do anything - if (which(file) === undefined) { + if ((await which(file)) === undefined) { throw new Error( `${file} is not on PATH. Please install it and add it to PATH.`, ); diff --git a/package/src/common/dependencies/pandoc.ts b/package/src/common/dependencies/pandoc.ts index 706d5775d07..41dc41d75c2 100644 --- a/package/src/common/dependencies/pandoc.ts +++ b/package/src/common/dependencies/pandoc.ts @@ -70,7 +70,7 @@ export function pandoc(version: string): Dependency { } } else { // verify that the binary is on PATH, but otherwise don't do anything - if (which(pandocBinary) === undefined) { + if ((await which(pandocBinary)) === undefined) { throw new Error( `${pandocBinary} is not on PATH. Please install it and add it to PATH.`, ); diff --git a/package/src/common/dependencies/typst-gather.ts b/package/src/common/dependencies/typst-gather.ts index cf805e5a617..aca041b657e 100644 --- a/package/src/common/dependencies/typst-gather.ts +++ b/package/src/common/dependencies/typst-gather.ts @@ -28,7 +28,7 @@ export function typstGather(version: string): Dependency { Deno.renameSync(extractedPath, join(targetDir, file)); } else { // verify that the binary is on PATH, but otherwise don't do anything - if (which(file) === undefined) { + if ((await which(file)) === undefined) { throw new Error( `${file} is not on PATH. Please install it and add it to PATH.`, ); diff --git a/package/src/common/dependencies/typst.ts b/package/src/common/dependencies/typst.ts index 1538f8f0055..27a046a4519 100644 --- a/package/src/common/dependencies/typst.ts +++ b/package/src/common/dependencies/typst.ts @@ -45,7 +45,7 @@ export function typst(version: string ): Dependency { } } else { // verify that the binary is on PATH, but otherwise don't do anything - if (which(file) === undefined) { + if ((await which(file)) === undefined) { throw new Error( `${file} is not on PATH. Please install it and add it to PATH.`, ); diff --git a/src/command/dev-call/typst-gather/cmd.ts b/src/command/dev-call/typst-gather/cmd.ts index abc51188b7d..4279f3c3575 100644 --- a/src/command/dev-call/typst-gather/cmd.ts +++ b/src/command/dev-call/typst-gather/cmd.ts @@ -40,10 +40,17 @@ export const typstGatherCommand = new Command() const typstGatherBinary = Deno.env.get("QUARTO_TYPST_GATHER") || architectureToolsPath(binaryName); if (!existsSync(typstGatherBinary)) { - error( - `typst-gather binary not found.\n` + - "Run ./configure.sh to build and install it.", - ); + error(`typst-gather binary not found at ${typstGatherBinary}.`); + if (Deno.env.get("QUARTO_TYPST_GATHER")) { + error( + "QUARTO_TYPST_GATHER is set but does not point to an existing file.", + ); + } else { + error( + "Run ./configure.sh to download it, or set QUARTO_TYPST_GATHER to " + + "the path of a typst-gather binary.", + ); + } Deno.exit(1); } diff --git a/src/core/typst-gather.ts b/src/core/typst-gather.ts index 8a4e9a45662..c50ca9700ad 100644 --- a/src/core/typst-gather.ts +++ b/src/core/typst-gather.ts @@ -37,8 +37,12 @@ export function typstGatherBinaryPath(): string { if (!existsSync(binary)) { throw new Error( - `typst-gather binary not found.\n` + - `Run ./configure.sh to build and install it.`, + `typst-gather binary not found at ${binary}.\n` + + (Deno.env.get("QUARTO_TYPST_GATHER") + ? `QUARTO_TYPST_GATHER is set but does not point to an existing file.` + : `Reinstall Quarto to restore the bundled binary, or set ` + + `QUARTO_TYPST_GATHER to the path of a typst-gather binary.\n` + + `In a Quarto source checkout, run ./configure.sh to download it.`), ); } From 064f927e7172b120c78f74ea681d393390fdf177 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 26 Jul 2026 22:16:29 +0000 Subject: [PATCH 2/2] changelog: add v1.11 backports section for #14731 Co-Authored-By: Claude Opus 5 --- news/changelog-1.10.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/news/changelog-1.10.md b/news/changelog-1.10.md index 69af732b322..ef87760eb5b 100644 --- a/news/changelog-1.10.md +++ b/news/changelog-1.10.md @@ -1,5 +1,13 @@ All changes included in 1.10: +# v1.11 backports + +## In this release + +- ([#14731](https://github.com/quarto-dev/quarto-cli/pull/14731)): Fix `QUARTO_VENDOR_BINARIES=false` builds silently succeeding with missing binaries. The `configure` step's "is it on PATH?" check for `typst-gather`, `typst`, `pandoc` and `esbuild` compared an unawaited `Promise` against `undefined`, so the guard never fired. A build with `typst-gather` neither vendored nor on PATH reported success and produced a Quarto tree without it, surfacing only at render time as `typst-gather analyze failed; staging all packages as fallback`. The missing-binary error message now also names the path that was checked and the `QUARTO_TYPST_GATHER` environment variable. + +# v1.10 changes + ## Regression fixes - ([#13583](https://github.com/quarto-dev/quarto-cli/issues/13583)): Fix "Show All Code" and "Hide All Code" in the `code-tools` menu doing nothing when `code-copy` is enabled. (author: @mcanouil)