mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-09 20:26:33 +00:00
`sphinx -b gettext -q -W` turns a warning into a failure, and `make i18n-extract` had been dying on 512 of them. 505 were one defect repeated: the README template emitted an Architecture section for every role, while `generate_architecture_page` writes ARCHITECTURE.md only for invokable ones. 101 roles therefore linked five times each to a page that does not exist. The section now carries the same `application_invokable` gate as the page, measured from 101 mismatched roles to 0. The remaining seven were separate markup defects, each fatal on its own under -W. architecture.py's four-entry block was an indented paragraph that docutils read as a definition list ending abruptly, so it becomes a literal block. branding.py held a double-backtick literal spanning four lines, which RST does not allow; it becomes a literal block too. container_extra_hosts.py wrote `Kwargs:`, which napoleon does not know - a blank line is measurably not enough, the section has to reach napoleon, so it is `Keyword Args:`. postgres_libraries.py needed one blank line before its bullet list, and the cleanup skill an H1 above its first `##`. conf.py gains two settings. autodoc_mock_imports covers `ansible.module_utils.kcadm_json`: that import is correct for an Ansible module, which resolves module_utils only inside a play, so autodoc on the control host is what has to yield, not the module. napoleon_use_ivar stops an `Attributes:` section from emitting `.. attribute::` directives that collide with the dataclass fields autodoc already documents - eight duplicate-object warnings, reproduced and cleared on demand. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
107 lines
3.2 KiB
Python
107 lines
3.2 KiB
Python
from __future__ import annotations
|
|
|
|
import logging
|
|
import os
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
from docutils import nodes
|
|
from pygments.lexers.templates import DjangoLexer
|
|
from sphinx.domains.python import PythonDomain
|
|
from sphinx.highlighting import lexers
|
|
|
|
logging.basicConfig(
|
|
level=logging.DEBUG if {"-v", "--verbose"} & set(sys.argv) else logging.INFO
|
|
)
|
|
logger = logging.getLogger(__name__)
|
|
|
|
CONF_DIR = Path(__file__).resolve().parent
|
|
|
|
templates_path = [str(CONF_DIR / "templates")]
|
|
html_static_path = [str(CONF_DIR / "assets")]
|
|
|
|
project = os.environ["DOCS_SPHINX_PROJECT"]
|
|
copyright = os.environ["DOCS_SPHINX_COPYRIGHT"] # noqa: A001
|
|
author = os.environ["DOCS_SPHINX_AUTHOR"]
|
|
|
|
_design_title = os.environ.get("DOCS_DESIGN_TITLE", "").strip()
|
|
if _design_title:
|
|
html_title = _design_title
|
|
|
|
lexers["jinja"] = DjangoLexer()
|
|
lexers["j2"] = DjangoLexer()
|
|
|
|
exclude_patterns = ["docs/build", "venv", "venv/**"]
|
|
|
|
autodoc_mock_imports = ["ansible.module_utils.kcadm_json"]
|
|
|
|
html_theme = "sphinxawesome_theme"
|
|
html_sidebars = {
|
|
"**": ["logo.html", "versions.html", "languages.html", "structure.html"]
|
|
}
|
|
html_context = {"docs_version": os.environ.get("DOCS_VERSION", "")}
|
|
html_favicon = "assets/img/favicon.ico"
|
|
html_theme_options = {
|
|
"show_prev_next": False,
|
|
"logo_light": "assets/img/logo.png",
|
|
"logo_dark": "assets/img/logo.png",
|
|
}
|
|
|
|
source_suffix = {
|
|
".md": "markdown",
|
|
".rst": "restructuredtext",
|
|
".yml": "yaml",
|
|
".yaml": "yaml",
|
|
}
|
|
|
|
extensions = [
|
|
"myst_parser",
|
|
"infinito_docs.extensions.local.file_headings",
|
|
"infinito_docs.extensions.local.subfolders",
|
|
"infinito_docs.extensions.roles_overview",
|
|
"infinito_docs.extensions.markdown_include",
|
|
"infinito_docs.extensions.parallel_postprocess",
|
|
"infinito_docs.extensions.untranslated.yaml",
|
|
"infinito_docs.extensions.untranslated.markup",
|
|
"infinito_docs.extensions.yaml_source",
|
|
"infinito_docs.extensions.directory_index",
|
|
"infinito_docs.extensions.mermaid",
|
|
"sphinx.ext.autodoc",
|
|
"sphinx.ext.napoleon",
|
|
]
|
|
|
|
autosummary_generate = True
|
|
napoleon_use_ivar = True
|
|
|
|
locale_dirs = ["locale"]
|
|
gettext_compact = "docs"
|
|
gettext_location = False
|
|
|
|
myst_enable_extensions = ["colon_fence"]
|
|
myst_heading_anchors = 6
|
|
myst_url_schemes = ("http", "https", "mailto", "ftp", "tel")
|
|
|
|
|
|
def replace_assets_in_doctree(app, doctree, docname):
|
|
for node in doctree.findall(nodes.image):
|
|
if "assets/" in node["uri"]:
|
|
node["uri"] = node["uri"].replace("assets/", "_static/")
|
|
logger.info("Replaced image URI in %s: %s", docname, node["uri"])
|
|
|
|
for node in doctree.findall(nodes.raw):
|
|
if node.get("format") == "html" and "assets/" in node.astext():
|
|
rewritten = node.astext().replace("assets/", "_static/")
|
|
node.children = [nodes.raw("", rewritten, format="html")]
|
|
logger.info("Replaced raw HTML assets in %s.", docname)
|
|
|
|
|
|
def setup(app):
|
|
app.connect("doctree-resolved", replace_assets_in_doctree)
|
|
|
|
python_domain = app.registry.domains.get(PythonDomain.name)
|
|
if python_domain is not None:
|
|
directive = python_domain.directives.get("currentmodule")
|
|
if directive is not None:
|
|
directive.optional_arguments = 10
|
|
return {"version": "1.0", "parallel_read_safe": True}
|