Skip to content

Add quarto install chrome-headless-shell command#14030

Merged
cderv merged 21 commits intomainfrom
chrome-headless-improvement
Feb 13, 2026
Merged

Add quarto install chrome-headless-shell command#14030
cderv merged 21 commits intomainfrom
chrome-headless-improvement

Conversation

@cderv
Copy link
Collaborator

@cderv cderv commented Feb 12, 2026

Summary

New quarto install chrome-headless-shell command that downloads Chrome Headless Shell from Google's Chrome for Testing API. This is a smaller, lighter headless browser purpose-built for automation — ideal for diagram rendering (Mermaid, Graphviz) to non-HTML formats.

Part of the Chrome Headless improvements epic (#11877).

What's included

  • Chrome for Testing download utilities that resolve platform-specific URLs via Google's JSON API (supports linux64, mac-arm64, mac-x64, win32, win64)
  • InstallableTool implementation registered in the tool system (quarto install, quarto uninstall, quarto update, quarto tools)
  • Browser discovery priority chain in getBrowserExecutablePath():
    1. QUARTO_CHROMIUM env var
    2. Quarto-installed chrome-headless-shell
    3. System Chrome/Edge
    4. Legacy puppeteer-managed Chromium revisions
  • quarto check integration showing chrome-headless-shell installation status
  • QUARTO_CHROMIUM validation — gracefully falls through when the path doesn't exist
  • 4 new smoke tests for Chrome-based diagram rendering (Mermaid PDF, Mermaid Typst, Mermaid multi-diagram, Graphviz PDF) with regex verification of screenshot output
  • 3 previously Linux-skipped smoke tests unlocked (no longer need setup-chrome in CI)
  • CI workflow updated: replaces setup-chrome action with quarto install chrome-headless-shell
  • Unit tests for both chrome-for-testing.ts and chrome-headless-shell.ts

Design rationale

Chrome Headless Shell is Google's recommended replacement for the --headless flag on full Chrome. It ships no UI, has fewer system dependencies, and is distributed through the stable Chrome for Testing API — making it more reliable than the legacy quarto install chromium approach which relied on Puppeteer's bundled Chromium revisions.

The priority chain preserves backward compatibility: users with QUARTO_CHROMIUM set or a system Chrome installed see no behavior change. The new tool slots in as a middle ground — lighter than system Chrome, more reliable than legacy chromium.

Test plan

  • Unit tests for CfT URL resolution and platform mapping
  • Unit tests for chrome-headless-shell install/uninstall lifecycle
  • Smoke tests: Mermaid → PDF, Mermaid → Typst, multi-diagram, Graphviz → PDF
  • quarto check displays chrome-headless-shell status
  • CI uses quarto install chrome-headless-shell instead of setup-chrome
  • CI smoke tests pass on Linux (this PR)

Part of #11877

Closes #10961, closes #6821, closes #13704

@posit-snyk-bot
Copy link
Collaborator

posit-snyk-bot commented Feb 12, 2026

Snyk checks have passed. No issues have been found so far.

Status Scanner Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues
Licenses 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

cderv and others added 12 commits February 12, 2026 21:05
Shared infrastructure for downloading binaries from the Google Chrome
for Testing (CfT) API. Provides platform detection, API fetching,
binary search, and download+extract functions that will be used by
installable tool implementations.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implement quarto install/uninstall lifecycle for chrome-headless-shell
using Chrome for Testing (CfT) infrastructure. Stores binaries under
quartoDataDir("chrome-headless-shell") with a plain text version file
matching the tinytex/verapdf pattern.

Key exports for downstream tasks:
- chromeHeadlessShellInstallable: InstallableTool registration
- chromeHeadlessShellExecutablePath(): browser discovery
- chromeHeadlessShellInstallDir(): install location

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… quarto check

- Register chrome-headless-shell in kInstallableTools, enabling
  `quarto install/uninstall chrome-headless-shell`
- Fix installableTools() to return registry keys instead of lowercased
  display names (broken for multi-word tool names)
- Add WSL handling: allow chrome-headless-shell install with info note
  (unlike full chromium which blocks on WSL)
- Insert chrome-headless-shell as priority 2 in getBrowserExecutablePath()
  between system Chrome and legacy puppeteer revisions
- Update error message to recommend chrome-headless-shell over chromium
- Add chrome-headless-shell detection in `quarto check install`
- Fix pre-existing bug: chromium lookup in quarto check used display name
  comparison that never matched due to casing; now uses registry key lookup

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The chromium fallback branch in chromeCb() accessed binDir and
installedVersion as properties but they are async functions on the
InstallableTool interface. This was pre-existing dead code (the name
comparison never matched), but now reachable after the registry key
lookup fix. Properly await both methods.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add if (chromiumVersion) guard before displaying version, consistent
with the chrome-headless-shell branch. Prevents showing literal
"undefined" if the version file is missing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add smoke tests exercising mermaid and graphviz rendering through
chrome-headless-shell (PDF, Typst, multi-diagram). Remove not_os:linux
restrictions from existing mermaid SVG tests since Chrome is now
available on all CI platforms. Replace browser-actions/setup-chrome
with quarto install chrome-headless-shell in CI workflow.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Check intermediate .tex/.typ files for \includegraphics and image()
references to mermaid-figure and dot-figure PNGs. This proves Chrome
actually produced screenshot images rather than only checking the
render didn't error.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Env var
- chrome-headless-shell
- system
- chromium
This should also close mermaid issues on CI
Extract browser detection logic into `detectChromeForCheck()` helper,
replacing the 4-branch if/else chain with a data object and single
formatting block. Fixes redundant `safeExistsSync` call and normalizes
JSON key from `QUARTO_CHROMIUM_invalid` to `warning`.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@cderv cderv force-pushed the chrome-headless-improvement branch from 19fe5d7 to aa4b99d Compare February 12, 2026 20:06
cderv and others added 5 commits February 13, 2026 12:31
`quarto update chrome-headless-shell` was treated as an extension name
because `resolveCompatibleArgs()` only recognized `tinytex` and `chromium`
as tool shortcuts. Replace hardcoded check with `installableTools()` so all
registered tools (including `chrome-headless-shell` and `verapdf`) are
recognized automatically.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use dynamic tool names in install/uninstall descriptions via
installableToolNames(). Add chrome-headless-shell examples and label
chromium as legacy in install, uninstall, and update commands.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The browser detection priority chain (QUARTO_CHROMIUM → chrome-headless-shell
→ system Chrome) was duplicated in `getBrowserExecutablePath()` and
`detectChromeForCheck()`. Extract the shared logic into `detectBrowser()` in
puppeteer.ts, keeping only the context-specific legacy fallbacks in each
caller.

Also add a known-path check in `findCftExecutable` before falling back to
`walkSync`, and bound the fallback walk to `maxDepth: 3`.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Make tool shortcut routing in resolveCompatibleArgs case-insensitive so
`quarto install TinyTeX` and similar mixed-case variants work. Align
install/uninstall/update descriptions to all list available tools
dynamically. Add debug logging in findCftExecutable fallback path.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
cderv and others added 4 commits February 13, 2026 16:48
`outputTools()` passed the display name (e.g. "Chrome Headless Shell")
to `toolSummary()`, which lowercases to "chrome headless shell" — not
the registry key "chrome-headless-shell". The lookup silently failed
and the tool was skipped.

Carry the registry key through `ToolInfo` by having `loadTools()`
iterate `installableTools()` keys directly. Use `tool.key` for
`toolSummary()` lookups, table display, and `selectTool()` values.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Tests hardcoded `linux64` or `win64` subdirectory names, so
`findCftExecutable` only hit the fast path on that one platform and
silently fell through to the walk fallback elsewhere. Use
`detectCftPlatform().platform` so the fast path is exercised on every OS.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
`quarto install` and `quarto uninstall` are tools-only commands.
Our branch incorrectly added "extension or" to their descriptions.
Restore tools-only wording while keeping the dynamic tool name list.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@cderv cderv merged commit 148d142 into main Feb 13, 2026
51 checks passed
@cderv cderv deleted the chrome-headless-improvement branch February 13, 2026 18:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants

Comments