mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-10 04:36:40 +00:00
Every role README carries a ```mermaid flowchart and the documentation showed it as source. A post-transform now replaces those literal blocks with <pre class="mermaid"> in html builds, which the runtime picks up; every other builder keeps the fence as it was. sphinxcontrib-mermaid injects an inline init script, which this deployment's CSP refuses, so the runtime and its initialisation are two linked files instead: mermaid-init.js calls mermaid.initialize with securityLevel strict and follows the reader's colour scheme. The diagram source is html-escaped before it is embedded, or an & or < in a node label would break the page. The runtime is fetched at image build time through ADD --checksum, so a changed upstream file fails the build rather than shipping. Version and digest are not ARG defaults, which test_arg_without_default forbids; they travel meta/services.yml -> vars/main.yml -> compose.yml.j2 the way DOCS_IMAGE and DOCS_VERSION already do, and an update block registers the pin with the npm version watcher instead of exempting it. Verified by building a page with sphinx: the diagram arrives as <pre class="mermaid"> with its source escaped, a bash fence stays a highlighted block, and both scripts are linked. A missing mermaid.min.js does not fail that build, so a local run without the image stays green. scripts/lint/mermaid.sh keeps validating diagram syntax through mmdc, which npm installs unpinned. It therefore checks against a newer mermaid than the 11.4.1 readers get, and a diagram using syntax from in between would pass the lint and render blank. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
100 lines
3.0 KiB
Python
100 lines
3.0 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 = "Infinito.Nexus - Cyber Master Infrastructure Solution"
|
|
copyright = "2025, Kevin Veen-Birkenbach" # noqa: A001
|
|
author = "Kevin Veen-Birkenbach"
|
|
|
|
lexers["jinja"] = DjangoLexer()
|
|
lexers["j2"] = DjangoLexer()
|
|
|
|
exclude_patterns = ["docs/build", "venv", "venv/**"]
|
|
|
|
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
|
|
|
|
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}
|