e940d071f0
Automated Testing / Code Quality (Lint & Format) (push) Has been cancelled
Automated Testing / Security Scan (push) Has been cancelled
Automated Testing / Type Checking (push) Has been cancelled
Automated Testing / Build EPUB Artifact (push) Has been cancelled
Automated Testing / Test Summary (push) Has been cancelled
Automated Testing / Unit Tests (Python 3.11) (push) Has been cancelled
Automated Testing / Unit Tests (Python 3.10) (push) Has been cancelled
Automated Testing / Unit Tests (Python 3.12) (push) Has been cancelled
Documentation Checks / Summary (push) Has been cancelled
Documentation Checks / Cross-reference Check (push) Has been cancelled
Documentation Checks / Check Links (push) Successful in 8m28s
Documentation Checks / Mermaid Syntax (push) Failing after 5m21s
Deploy Website to GitHub Pages / Deploy to GitHub Pages (push) Has been cancelled
Deploy Website to GitHub Pages / Build static site (push) Has been cancelled
Documentation Checks / Markdown Linting (push) Has been cancelled
87 lines
2.7 KiB
Python
87 lines
2.7 KiB
Python
#!/usr/bin/env python3
|
|
"""Validate Mermaid diagram syntax in Markdown files using mmdc."""
|
|
|
|
import json
|
|
import os
|
|
import re
|
|
import shutil
|
|
import subprocess # nosec B404
|
|
import sys
|
|
import tempfile
|
|
from pathlib import Path
|
|
|
|
IGNORE_DIRS = {".venv", "node_modules", ".git", "blog-posts", ".agents"}
|
|
|
|
|
|
def main() -> int:
|
|
if not shutil.which("mmdc"):
|
|
print(
|
|
"⚠ mmdc not found — skipping Mermaid validation (install @mermaid-js/mermaid-cli)"
|
|
)
|
|
return 0
|
|
|
|
errors = []
|
|
checked = 0
|
|
|
|
# On GitHub Actions Linux runners, Chrome/Puppeteer requires --no-sandbox.
|
|
# Write a temporary puppeteer config when MERMAID_PUPPETEER_NO_SANDBOX is set.
|
|
puppeteer_config_path = None
|
|
extra_args: list[str] = []
|
|
if os.environ.get("MERMAID_PUPPETEER_NO_SANDBOX") == "true":
|
|
with tempfile.NamedTemporaryFile(
|
|
suffix=".json", mode="w", delete=False
|
|
) as pcfg:
|
|
json.dump({"args": ["--no-sandbox", "--disable-setuid-sandbox"]}, pcfg)
|
|
puppeteer_config_path = pcfg.name
|
|
extra_args = ["-p", puppeteer_config_path]
|
|
|
|
md_files = [
|
|
f
|
|
for f in Path().rglob("*.md")
|
|
if not any(part in IGNORE_DIRS for part in f.parts)
|
|
]
|
|
|
|
try:
|
|
for file_path in md_files:
|
|
content = file_path.read_text()
|
|
blocks = re.findall(r"```mermaid\n(.*?)```", content, re.DOTALL)
|
|
for i, block in enumerate(blocks):
|
|
with tempfile.NamedTemporaryFile(
|
|
suffix=".mmd", mode="w", delete=False
|
|
) as tmp:
|
|
tmp.write(block)
|
|
tmp_path = tmp.name
|
|
out_path = str(Path(tmp_path).with_suffix(".svg"))
|
|
try:
|
|
result = subprocess.run( # nosec B603 B607
|
|
["mmdc", "-i", tmp_path, "-o", out_path, *extra_args],
|
|
capture_output=True,
|
|
text=True,
|
|
check=False,
|
|
)
|
|
if result.returncode != 0:
|
|
errors.append(
|
|
f"{file_path} (block {i + 1}): {result.stderr.strip()}"
|
|
)
|
|
else:
|
|
checked += 1
|
|
finally:
|
|
Path(tmp_path).unlink(missing_ok=True)
|
|
Path(out_path).unlink(missing_ok=True)
|
|
finally:
|
|
if puppeteer_config_path:
|
|
Path(puppeteer_config_path).unlink(missing_ok=True)
|
|
|
|
print(f"✅ Checked {checked} Mermaid diagram(s)")
|
|
if errors:
|
|
print("\n❌ Mermaid errors:")
|
|
for e in errors:
|
|
print(f" - {e}")
|
|
return 1
|
|
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|