From 59aa3f1d86eae5cbbf36ce440e071655d1446a28 Mon Sep 17 00:00:00 2001 From: KaifAhmad1 Date: Sat, 20 Jun 2026 16:28:02 +0530 Subject: [PATCH] docs: add mintlify export + JSX balance checks to docs_check.py; run on PRs --- .github/workflows/docs.yml | 3 ++ docs/changelog.md | 4 +-- docs_check.py | 67 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 3 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index e24c0dd7..8a0a8738 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -33,6 +33,9 @@ jobs: - uses: actions/setup-python@v5 with: python-version: '3.11' + - uses: actions/setup-node@v6 + with: + node-version: '20' - run: python docs_check.py deploy: diff --git a/docs/changelog.md b/docs/changelog.md index 8a861dad..a125a2d0 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -3,9 +3,7 @@ title: "Changelog" description: "Release history for Semantica. All notable changes by version." --- - - The latest stable release is **v0.5.0**. Changes listed under **Unreleased** are merged to `main` but not yet published to PyPI. - +The latest stable release is **v0.5.0**. Changes listed under **Unreleased** are merged to `main` but not yet published to PyPI. --- diff --git a/docs_check.py b/docs_check.py index eb24daa1..5989cfe8 100644 --- a/docs_check.py +++ b/docs_check.py @@ -5,6 +5,7 @@ import glob import json import os import re +import subprocess import sys from typing import Any, Callable, cast @@ -195,6 +196,72 @@ def _() -> list[str]: return [m for m in modules if m not in index] +# ── 9. JSX component tags are balanced in every page ───────────────────────── +@check("All Mintlify JSX component tags are balanced") +def _() -> list[str]: + # Paired block-level components that must open and close. + COMPONENTS = [ + "AccordionGroup", "Accordion", "Steps", "Step", + "CodeGroup", "Tabs", "Tab", "CardGroup", "Card", + "Expandable", + ] + issues: list[str] = [] + for fpath in ALL_MD: + content = read(fpath) + lines = content.splitlines() + for comp in COMPONENTS: + # Count opening and closing tags (ignore self-closing ) + opens = len(re.findall(rf"<{comp}[\s>]", content)) + closes = len(re.findall(rf"", content)) + if opens != closes: + issues.append( + f"{fpath}: <{comp}> opened {opens}x but closed {closes}x" + ) + # Check code fences are balanced (odd fence count = unclosed block) + fence_count = sum( + 1 for ln in lines if ln.strip().startswith("```") + ) + if fence_count % 2 != 0: + issues.append(f"{fpath}: odd number of ``` fences — unclosed code block") + return issues + + +# ── 10. Mintlify export succeeds (requires Node.js / npx) ──────────────────── +@check("Mintlify export builds without errors") +def _() -> list[str]: + npx = "npx.cmd" if sys.platform == "win32" else "npx" + try: + result = subprocess.run( + [npx, "--yes", "mintlify@4.2.632", "export", "--output", "export_ci_check.zip"], + cwd=DOCS, + capture_output=True, + text=True, + timeout=300, + ) + # Clean up zip regardless of outcome + zip_path = os.path.join(DOCS, "export_ci_check.zip") + if os.path.exists(zip_path): + os.remove(zip_path) + + combined = (result.stdout or "") + (result.stderr or "") + + if result.returncode != 0: + # On Windows, npm cleanup raises EPERM on temp dirs — not a real + # export failure. Treat as a skip rather than a hard failure. + if sys.platform == "win32" and "EPERM" in combined and \ + "could not be generated" not in combined: + return [] # Windows temp-cleanup noise; real CI runs on Linux + + # Surface the last 20 lines so the failing page error is visible + tail = "\n".join(combined.strip().splitlines()[-20:]) + return [f"mintlify export failed (exit {result.returncode}):\n{tail}"] + return [] + except FileNotFoundError: + return ["npx not found — skipping Mintlify export check (Node.js required)"] + except subprocess.TimeoutExpired: + return ["mintlify export timed out after 300 s"] + + # ── Summary ─────────────────────────────────────────────────────────────────── _print_summary(failures) if failures: