Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/user_guide/13-building.qmd
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@ messages. Common causes include:
between `---` markers and that the YAML is well-formed.
- A reference to a file that does not exist. If you renamed or deleted a user guide page, make
sure the `guide-section` frontmatter in other files does not depend on it.
- A Quarto version incompatibility. Great Docs is tested with Quarto 1.4 and later. Run
- A Quarto version incompatibility. Great Docs requires Quarto 1.9.18 or later. Run
`quarto --version` to check your installed version.

### Missing API Reference Pages
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
title: Markdown pages
author: Great Docs
version: 1.0.0
quarto-required: ">=1.3.0"
quarto-required: ">=1.9.18"
contributes:
filters:
- path: markdown-pages.lua
Expand Down
71 changes: 62 additions & 9 deletions great_docs/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import os
import re
import shutil
import subprocess
import sys
from concurrent.futures import ThreadPoolExecutor
from datetime import datetime
Expand Down Expand Up @@ -91,16 +92,68 @@ class QuartoNotFoundError(RuntimeError):
"""Raised when the Quarto CLI is not available on the system."""


class QuartoVersionError(RuntimeError):
"""Raised when the installed Quarto CLI is older than great-docs supports."""


# The bundled extensions set the stage at which their filters run, which
# Quarto's extension schema accepts from this release
MIN_QUARTO_VERSION = (1, 9, 18)


def _quarto_version() -> tuple[int, ...] | None:
"""
Read the version of the Quarto CLI on the `PATH`

Returns
-------
:
The release numbers, or `None` when Quarto does not report a
recognisable version.
"""
try:
proc = subprocess.run(
["quarto", "--version"],
capture_output=True,
timeout=30,
check=True,
**TEXT_MODE_KWARGS,
)
except (OSError, subprocess.SubprocessError):
return None
match = re.match(r"\s*(\d+(?:\.\d+)*)", proc.stdout)
return tuple(int(part) for part in match.group(1).split(".")) if match else None


def _ensure_quarto_installed() -> None:
if shutil.which("quarto") is not None:
return
raise QuartoNotFoundError(
"Quarto was not found on your system. Quarto is a system-level "
"dependency of great-docs and is not bundled with the Python package.\n\n"
"See the troubleshooting guide:\n"
" https://posit-dev.github.io/great-docs/recipes/fix-common-build-errors.html\n\n"
"Or fetch the binary programmatically by using your OS package (brew, yum, winget)."
)
"""
Check that a supported Quarto CLI is on the `PATH`

A Quarto whose version cannot be read passes the check.

Raises
------
QuartoNotFoundError
If Quarto is not on the `PATH`.
QuartoVersionError
If Quarto is older than `MIN_QUARTO_VERSION`.
"""
if shutil.which("quarto") is None:
raise QuartoNotFoundError(
"Quarto was not found on your system. Quarto is a system-level "
"dependency of great-docs and is not bundled with the Python package.\n\n"
"See the troubleshooting guide:\n"
" https://posit-dev.github.io/great-docs/recipes/fix-common-build-errors.html\n\n"
"Or fetch the binary programmatically by using your OS package (brew, yum, winget)."
)
version = _quarto_version()
if version is not None and version < MIN_QUARTO_VERSION:
found = ".".join(map(str, version))
required = ".".join(map(str, MIN_QUARTO_VERSION))
raise QuartoVersionError(
f"great-docs requires Quarto {required} or later, but found Quarto {found}. "
"Upgrade Quarto from https://quarto.org/docs/get-started/"
)


def _migrate_legacy_gitignore_entry(content: str) -> str:
Expand Down
5 changes: 4 additions & 1 deletion tests/test_great_docs.py
Original file line number Diff line number Diff line change
Expand Up @@ -43648,7 +43648,10 @@ def test_ensure_quarto_installed_passes_when_present():
"""_ensure_quarto_installed returns None when quarto is found."""
from great_docs.core import _ensure_quarto_installed

with patch("shutil.which", return_value="/usr/bin/quarto"):
with (
patch("shutil.which", return_value="/usr/bin/quarto"),
patch("great_docs.core._quarto_version", return_value=(1, 10, 0)),
):
result = _ensure_quarto_installed()
assert result is None

Expand Down
17 changes: 16 additions & 1 deletion tests/test_quarto_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import pytest

from great_docs import GreatDocs
from great_docs.core import QuartoNotFoundError, _ensure_quarto_installed
from great_docs.core import QuartoNotFoundError, QuartoVersionError, _ensure_quarto_installed


TROUBLESHOOTING_URL = "https://posit-dev.github.io/great-docs/recipes/fix-common-build-errors.html"
Expand All @@ -16,6 +16,21 @@ def test_ensure_quarto_installed_passes_when_present(monkeypatch):
"great_docs.core.shutil.which",
lambda name: "/usr/local/bin/quarto" if name == "quarto" else None,
)
monkeypatch.setattr("great_docs.core._quarto_version", lambda: (1, 10, 0))
assert _ensure_quarto_installed() is None


def test_ensure_quarto_installed_raises_when_too_old(monkeypatch):
monkeypatch.setattr("great_docs.core.shutil.which", lambda name: "/usr/local/bin/quarto")
monkeypatch.setattr("great_docs.core._quarto_version", lambda: (1, 8, 26))

with pytest.raises(QuartoVersionError, match=r"1\.9\.18 or later, but found Quarto 1\.8\.26"):
_ensure_quarto_installed()


def test_ensure_quarto_installed_passes_when_version_unreadable(monkeypatch):
monkeypatch.setattr("great_docs.core.shutil.which", lambda name: "/usr/local/bin/quarto")
monkeypatch.setattr("great_docs.core._quarto_version", lambda: None)
assert _ensure_quarto_installed() is None


Expand Down
Loading