From 54fb9c370198c74e6b960e0d4c3de1a71d034c71 Mon Sep 17 00:00:00 2001 From: Michael Johnson Date: Sat, 17 Oct 2026 05:35:37 -0400 Subject: [PATCH] fix(docs): GH alerts render as admonitions on the site. --- dbuild/docs.py | 22 ++++++++++++++++++++++ dbuild/templates/README.mkdocs.j2 | 2 ++ tests/test_docs.py | 15 +++++++++++++++ 3 files changed, 39 insertions(+) diff --git a/dbuild/docs.py b/dbuild/docs.py index 6457bfd..bece8e8 100644 --- a/dbuild/docs.py +++ b/dbuild/docs.py @@ -329,6 +329,24 @@ def _site_placeholders(text: str, env_file: bool = False) -> str: MAX_COMBOS = 16 +# GitHub's alert blockquote (> [!WARNING]) -> the site's admonition. The site +# renders the alert as a quote with "[!WARNING]" in it. +_GITHUB_ALERT_RE = re.compile(r"(?m)^> ?\[!(NOTE|TIP|IMPORTANT|WARNING|CAUTION)\][ \t]*\n((?:>.*(?:\n|$))*)") +_ADMONITION = {"NOTE": "note", "TIP": "tip", "IMPORTANT": 'info "Important"', "WARNING": "warning", "CAUTION": 'danger "Caution"'} + + +def _site_alerts(text: Any) -> Any: + """Markdown with GitHub alerts as admonitions, for the site's render.""" + if not isinstance(text, str): + return text + + def admonition(m: re.Match) -> str: + body = [re.sub(r"^> ?", "", ln) for ln in m.group(2).splitlines()] + return f"!!! {_ADMONITION[m.group(1)]}\n\n" + "".join(f" {ln}\n" if ln.strip() else "\n" for ln in body) + + return _GITHUB_ALERT_RE.sub(admonition, text) + + def _combination_count(cfg: Config) -> int: import math @@ -679,6 +697,10 @@ def _enrich_metadata(cfg: Config, community_override: str | None = None) -> dict "healthcheck": meta.healthcheck, "notes": meta.notes, "readme": meta.readme, + # The same for README.mkdocs.j2, which swaps them in. + "notes_site": _site_alerts(meta.notes), + "readme_site": dataclasses.replace(meta.readme, **{f.name: _site_alerts(getattr(meta.readme, f.name)) for f in dataclasses.fields(meta.readme)}) + if dataclasses.is_dataclass(meta.readme) else meta.readme, "community": community_raw, "community_name": community_name, "community_url": community_url, diff --git a/dbuild/templates/README.mkdocs.j2 b/dbuild/templates/README.mkdocs.j2 index 3c3cc2b..a66b279 100644 --- a/dbuild/templates/README.mkdocs.j2 +++ b/dbuild/templates/README.mkdocs.j2 @@ -1,4 +1,6 @@ {% extends "README.j2" %} +{#- GitHub alerts in the author's text become admonitions here (_site_alerts). #} +{%- set readme, notes = readme_site, notes_site %} {#- What an option runs, before any YAML: the images, then the host folders the data lands in. Plain words; markdown, not a raw HTML block (a block diff --git a/tests/test_docs.py b/tests/test_docs.py index 9ca8eb3..b84fa3d 100644 --- a/tests/test_docs.py +++ b/tests/test_docs.py @@ -1066,3 +1066,18 @@ def test_past_the_cap_only_the_defaults(self): self.assertNotIn("Left out", readme.split("### Podman Compose", 1)[1].split("###", 1)[0], "no heading per combination") self.assertLess(len(readme), 50_000) + + +class TestSiteAlerts(unittest.TestCase): + def test_github_alert_becomes_an_admonition(self): + md = "intro\n\n> [!WARNING]\n> **Careful.** It breaks.\n>\n> Keep a backup.\n\nafter\n" + self.assertEqual(docs._site_alerts(md), + "intro\n\n!!! warning\n\n **Careful.** It breaks.\n\n Keep a backup.\n\nafter\n") + + def test_a_plain_quote_is_left_alone(self): + md = "> Just a quote.\n" + self.assertEqual(docs._site_alerts(md), md) + + def test_important_and_caution_keep_their_word(self): + self.assertTrue(docs._site_alerts("> [!IMPORTANT]\n> x\n").startswith('!!! info "Important"\n')) + self.assertTrue(docs._site_alerts("> [!CAUTION]\n> x\n").startswith('!!! danger "Caution"\n'))