Build the MkDocs documentation site into the published static tree.
The documentation (docs/ + mkdocs.yml at the repository root) is a
phase of site build: it renders into <site-output>/docs/ so the
website serves it under /docs/ from the same tree as everything else,
staging builds (--site-output-dir) never touch the live dist/docs,
and the precompress phase covers the rendered HTML like any other asset.
MkDocs is an optional dependency group (uv sync --group docs; installed
by default through [tool.uv] default-groups). The publisher imports it
lazily so the rest of the CLI works without it and site build can fail
fast, before the multi-hour cache phases, when the toolchain is missing.
Bases: RuntimeError
The MkDocs toolchain is not importable in this environment.
DocsBuildError
Bases: RuntimeError
The MkDocs build failed (configuration error or strict-mode warnings).
ensure_docs_tooling_available() -> None
Raise :class:DocsToolingUnavailable unless every docs plugin imports.
Source code in src/mamut_routing_publish/docs_build.py
| def ensure_docs_tooling_available() -> None:
"""Raise :class:`DocsToolingUnavailable` unless every docs plugin imports."""
import importlib
missing: list[str] = []
for module_name in _REQUIRED_MODULES:
try:
importlib.import_module(module_name)
except ImportError:
missing.append(module_name)
if missing:
raise DocsToolingUnavailable(
"The MkDocs documentation toolchain is not installed "
f"(missing: {', '.join(missing)}). Run `uv sync --group docs`, "
"or pass --skip-docs to `site build`."
)
|
has_docs_config
has_docs_config(repo_root: Path) -> bool
True when the checkout carries a documentation site to build.
Source code in src/mamut_routing_publish/docs_build.py
| def has_docs_config(repo_root: Path) -> bool:
"""True when the checkout carries a documentation site to build."""
return (repo_root / DEFAULT_MKDOCS_CONFIG_NAME).is_file()
|
build_docs_site
build_docs_site(repo_root: Path, site_dir: Path, *, strict: bool = True, quiet: bool = False, config_file: Path | None = None) -> DocsBuildSummary
Render mkdocs.yml into site_dir (cleaned first by MkDocs itself).
quiet silences the console only: strict-mode warnings are still
counted and still fail the build.
Runs with the repository root as the working directory: the gen-files
script, the mkdocstrings source paths and the snippet base path in
mkdocs.yml are all relative to it. Strict mode turns every MkDocs
warning (missing nav target, broken link, unresolved reference) into a
:class:DocsBuildError, which is what CI and the publisher want.
Source code in src/mamut_routing_publish/docs_build.py
| def build_docs_site(
repo_root: Path,
site_dir: Path,
*,
strict: bool = True,
quiet: bool = False,
config_file: Path | None = None,
) -> DocsBuildSummary:
"""Render ``mkdocs.yml`` into ``site_dir`` (cleaned first by MkDocs itself).
``quiet`` silences the console only: strict-mode warnings are still
counted and still fail the build.
Runs with the repository root as the working directory: the gen-files
script, the mkdocstrings source paths and the snippet base path in
``mkdocs.yml`` are all relative to it. Strict mode turns every MkDocs
warning (missing nav target, broken link, unresolved reference) into a
:class:`DocsBuildError`, which is what CI and the publisher want.
"""
ensure_docs_tooling_available()
from mkdocs.commands.build import build
from mkdocs.config import load_config
from mkdocs.exceptions import MkDocsException
config_path = config_file or (repo_root / DEFAULT_MKDOCS_CONFIG_NAME)
if not config_path.is_file():
raise DocsBuildError(f"MkDocs configuration not found: {config_path}")
site_dir = site_dir if site_dir.is_absolute() else repo_root / site_dir
site_dir.parent.mkdir(parents=True, exist_ok=True)
mkdocs_logger = logging.getLogger("mkdocs")
previous_propagate = mkdocs_logger.propagate
quiet_handler: logging.Handler | None = None
if quiet:
# MkDocs counts warnings for strict mode through a handler it attaches
# to this logger inside build(); records must therefore keep flowing.
# Quiet only stops them from reaching the console (root handlers or
# logging's last-resort stderr handler).
mkdocs_logger.propagate = False
quiet_handler = logging.NullHandler()
mkdocs_logger.addHandler(quiet_handler)
# A plugin in the toolchain prints a long advisory about the MkDocs 2.0
# transition on every build; it is noise for a pinned, tested toolchain.
previous_advisory = os.environ.get(_MKDOCS_ADVISORY_ENV)
os.environ[_MKDOCS_ADVISORY_ENV] = "true"
started_at = time.perf_counter()
try:
with _working_directory(repo_root):
config = load_config(config_file=str(config_path), site_dir=str(site_dir), strict=strict)
build(config)
except MkDocsException as error:
raise DocsBuildError(f"mkdocs build failed: {error}") from error
finally:
mkdocs_logger.propagate = previous_propagate
if quiet_handler is not None:
mkdocs_logger.removeHandler(quiet_handler)
if previous_advisory is None:
os.environ.pop(_MKDOCS_ADVISORY_ENV, None)
else:
os.environ[_MKDOCS_ADVISORY_ENV] = previous_advisory
html_files_written = sum(1 for path in site_dir.rglob("*.html") if path.is_file())
return DocsBuildSummary(
site_dir=site_dir,
html_files_written=html_files_written,
wall_time_seconds=time.perf_counter() - started_at,
strict=strict,
)
|