From 257838dc9f7624373013e17915e8b8bb6d805a13 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 --- news/changelog-1.11.md | 2 ++ 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 ++++++-- 7 files changed, 23 insertions(+), 10 deletions(-) diff --git a/news/changelog-1.11.md b/news/changelog-1.11.md index 4c11a6001e4..c84a42d3b09 100644 --- a/news/changelog-1.11.md +++ b/news/changelog-1.11.md @@ -1,3 +1,5 @@ All changes included in 1.11: +## Regression fixes +- ([#PRNUM](https://github.com/quarto-dev/quarto-cli/pull/PRNUM)): 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. 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 9a256a7a5cd5856d12f7f0e2ecadc40d23e28e98 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 26 Jul 2026 22:09:43 +0000 Subject: [PATCH 2/2] changelog: reference PR number Co-Authored-By: Claude Opus 5 --- news/changelog-1.11.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/news/changelog-1.11.md b/news/changelog-1.11.md index c84a42d3b09..4bdbaf50d21 100644 --- a/news/changelog-1.11.md +++ b/news/changelog-1.11.md @@ -2,4 +2,4 @@ All changes included in 1.11: ## Regression fixes -- ([#PRNUM](https://github.com/quarto-dev/quarto-cli/pull/PRNUM)): 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. +- ([#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.