From c781db9cd02e07a22cae83ed797d7ff9fcb27077 Mon Sep 17 00:00:00 2001 From: WuXie Date: Fri, 27 Mar 2026 09:23:32 +0800 Subject: [PATCH 1/4] docs(ci): add non-executing build-docs command checks --- .../workflows/docs-build-command-check.yml | 19 +++++ tools/check_build_docs_commands.py | 78 +++++++++++++++++++ 2 files changed, 97 insertions(+) create mode 100644 .github/workflows/docs-build-command-check.yml create mode 100755 tools/check_build_docs_commands.py diff --git a/.github/workflows/docs-build-command-check.yml b/.github/workflows/docs-build-command-check.yml new file mode 100644 index 0000000000..1d5c4cac55 --- /dev/null +++ b/.github/workflows/docs-build-command-check.yml @@ -0,0 +1,19 @@ +name: docs-build-command-check + +on: + pull_request: + push: + branches: [master] + +jobs: + docs-build-command-check: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.x' + - name: Run build-docs command checks (non-executing) + run: python3 tools/check_build_docs_commands.py diff --git a/tools/check_build_docs_commands.py b/tools/check_build_docs_commands.py new file mode 100755 index 0000000000..4af11f219a --- /dev/null +++ b/tools/check_build_docs_commands.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python3 +from __future__ import annotations +import re +from pathlib import Path +import subprocess +import tempfile + +ROOT = Path(__file__).resolve().parents[1] +DOCS = [ + ROOT / "docs" / "variants" / "overview.md", + ROOT / "docs" / "variants" / "building-manual.md", + ROOT / "docs" / "variants" / "building-uefi.md", +] +BLOCK_RE = re.compile(r"```(?:bash|sh|shell)?\n(.*?)```", re.S) +ALLOWED_PREFIXES = ( + "git ", "make", "cmake", "docker", "podman", "python", "python3", "pip", "pip3", + "./", "sudo ", "export ", "cd ", "echo ", "cat ", "ls", "mkdir", "rm ", "cp ", "mv ", + "source ", "ninja", "west", "repo ", "curl ", "wget ", "tar ", "xz ", "unzip ", +) + +def extract_blocks(text: str): + for m in BLOCK_RE.finditer(text): + b = m.group(1).strip() + if b: + yield b + +def check_bash_syntax(block: str): + with tempfile.NamedTemporaryFile("w", suffix=".sh", delete=True) as f: + f.write(block + "\n") + f.flush() + p = subprocess.run(["bash", "-n", f.name], capture_output=True, text=True) + return p.returncode == 0, (p.stderr or p.stdout).strip() + +def is_cmd(line: str): + s = line.strip() + if not s or s.startswith("#"): + return False + if s in ("fi", "done", "then", "do", "esac", "else"): + return False + return True + +def main() -> int: + failures = [] + warns = [] + tested = 0 + for doc in DOCS: + if not doc.exists(): + warns.append((str(doc), 0, "file missing; skipped")) + continue + text = doc.read_text(encoding="utf-8", errors="ignore") + for i, block in enumerate(extract_blocks(text), 1): + tested += 1 + ok, err = check_bash_syntax(block) + if not ok: + failures.append((str(doc), i, err)) + for ln, line in enumerate(block.splitlines(), 1): + s = line.strip() + if not is_cmd(s): + continue + if s.startswith(("if ", "for ", "while ", "case ")): + continue + if not s.startswith(ALLOWED_PREFIXES): + warns.append((str(doc), i, f"line {ln}: uncommon prefix -> {s}")) + print(f"[docs-check] tested blocks: {tested}") + if warns: + print("[docs-check] warnings:") + for d, i, w in warns: + print(f" - {d} block#{i}: {w}") + if failures: + print("[docs-check] failures:") + for d, i, e in failures: + print(f" - {d} block#{i}: {e}") + return 1 + print("[docs-check] OK") + return 0 + +if __name__ == '__main__': + raise SystemExit(main()) From 969beb508b7930debdc7efa349dbceb933373cb0 Mon Sep 17 00:00:00 2001 From: WuXie Date: Fri, 27 Mar 2026 09:24:06 +0800 Subject: [PATCH 2/4] docs(ci): scan variant build manuals for shell block syntax --- tools/check_build_docs_commands.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tools/check_build_docs_commands.py b/tools/check_build_docs_commands.py index 4af11f219a..e6d8387da0 100755 --- a/tools/check_build_docs_commands.py +++ b/tools/check_build_docs_commands.py @@ -6,11 +6,8 @@ import tempfile ROOT = Path(__file__).resolve().parents[1] -DOCS = [ - ROOT / "docs" / "variants" / "overview.md", - ROOT / "docs" / "variants" / "building-manual.md", - ROOT / "docs" / "variants" / "building-uefi.md", -] +# Focus on build docs pages across variants +DOCS = sorted((ROOT / "docs" / "variants").glob("**/building-manual.md")) BLOCK_RE = re.compile(r"```(?:bash|sh|shell)?\n(.*?)```", re.S) ALLOWED_PREFIXES = ( "git ", "make", "cmake", "docker", "podman", "python", "python3", "pip", "pip3", @@ -43,10 +40,11 @@ def main() -> int: failures = [] warns = [] tested = 0 + if not DOCS: + print("[docs-check] no build-manual.md files found") + return 1 + for doc in DOCS: - if not doc.exists(): - warns.append((str(doc), 0, "file missing; skipped")) - continue text = doc.read_text(encoding="utf-8", errors="ignore") for i, block in enumerate(extract_blocks(text), 1): tested += 1 @@ -61,6 +59,8 @@ def main() -> int: continue if not s.startswith(ALLOWED_PREFIXES): warns.append((str(doc), i, f"line {ln}: uncommon prefix -> {s}")) + + print(f"[docs-check] scanned files: {len(DOCS)}") print(f"[docs-check] tested blocks: {tested}") if warns: print("[docs-check] warnings:") From 05a5894451824195df24fea32f0b36abff4c2dad Mon Sep 17 00:00:00 2001 From: WuXie Date: Fri, 27 Mar 2026 09:24:17 +0800 Subject: [PATCH 3/4] docs(ci): skip placeholder pseudocode blocks in command check --- tools/check_build_docs_commands.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tools/check_build_docs_commands.py b/tools/check_build_docs_commands.py index e6d8387da0..55ca5625ae 100755 --- a/tools/check_build_docs_commands.py +++ b/tools/check_build_docs_commands.py @@ -21,6 +21,12 @@ def extract_blocks(text: str): if b: yield b + +def should_skip_block(block: str) -> bool: + b = block.lower() + markers = ["", "", "(docker)", ""] + return any(m in b for m in markers) + def check_bash_syntax(block: str): with tempfile.NamedTemporaryFile("w", suffix=".sh", delete=True) as f: f.write(block + "\n") @@ -48,6 +54,10 @@ def main() -> int: text = doc.read_text(encoding="utf-8", errors="ignore") for i, block in enumerate(extract_blocks(text), 1): tested += 1 + if should_skip_block(block): + warns.append((str(doc), i, "placeholder/pseudocode block skipped")) + continue + ok, err = check_bash_syntax(block) if not ok: failures.append((str(doc), i, err)) From 52f6692752fa24f496e11fd3cf004ec7b4fb3644 Mon Sep 17 00:00:00 2001 From: WuXie Date: Fri, 27 Mar 2026 09:41:40 +0800 Subject: [PATCH 4/4] ci: add YAML document start for workflow lint --- .github/workflows/docs-build-command-check.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/docs-build-command-check.yml b/.github/workflows/docs-build-command-check.yml index 1d5c4cac55..1106959beb 100644 --- a/.github/workflows/docs-build-command-check.yml +++ b/.github/workflows/docs-build-command-check.yml @@ -1,3 +1,4 @@ +--- name: docs-build-command-check on: