Full-project-review remediation: Waves 1–4 (H2→N1, 78 commits) #119
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Validate user-manuals | |
| # On pull requests that touch the user-manual sources or the build tool, | |
| # verify the build script can compile every markdown file without errors | |
| # and that every page declared in `_meta.yaml` has at least an English | |
| # source. Catches structural problems before they hit main. | |
| on: | |
| push: | |
| branches: [main, development] | |
| paths: | |
| - "docs/usermanuals/**" | |
| - "tools/build_usermanuals.py" | |
| pull_request: | |
| branches: [main] | |
| paths: | |
| - "docs/usermanuals/**" | |
| - "tools/build_usermanuals.py" | |
| # Mirror CI's operator identity so any future pytest invocation does not | |
| # raise on distroless runners where USER is unset. | |
| env: | |
| FORGELM_OPERATOR: ci-smoke | |
| jobs: | |
| validate: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - uses: actions/setup-python@v6 | |
| with: | |
| python-version: "3.11" | |
| - name: Install dependencies | |
| run: pip install markdown pyyaml | |
| - name: Build user-manuals | |
| run: python3 tools/build_usermanuals.py | |
| - name: Smoke-test that core pages have English content | |
| # If somebody renamed a section in _meta.yaml without moving the | |
| # markdown source, the build silently falls back to a placeholder. | |
| # Fail the build instead so the PR author notices. | |
| run: | | |
| python3 - <<'PY' | |
| import json, pathlib, re | |
| src = pathlib.Path("site/js/usermanuals/en.js").read_text(encoding="utf-8") | |
| # Each "missing": true entry is a page whose markdown source | |
| # could not be found at all (no English fallback even). | |
| missing = re.findall(r'"([^"]+)":\s*\{[^{}]*"missing":\s*true', src) | |
| if missing: | |
| print("ERROR: pages declared in _meta.yaml but missing markdown:") | |
| for m in sorted(set(missing)): | |
| print(f" - {m}") | |
| raise SystemExit(1) | |
| print("OK — every declared page has an English source.") | |
| PY | |
| - name: Orphan-markdown detection (strict) | |
| # The reverse of the smoke test above: a markdown file under | |
| # docs/usermanuals/<lang>/<section>/<page>.md that is NOT declared | |
| # in _meta.yaml will silently never appear on the site. Strict | |
| # mode fails the build so abandoned drafts are caught before | |
| # they merge to main. | |
| run: | | |
| python3 - <<'PY' | |
| import pathlib, sys, yaml | |
| meta = yaml.safe_load(pathlib.Path("docs/usermanuals/_meta.yaml").read_text(encoding="utf-8")) | |
| declared = set() | |
| for sec in meta.get("sections", []): | |
| for page in sec.get("pages", []): | |
| declared.add(f"{sec['id']}/{page['id']}.md") | |
| root = pathlib.Path("docs/usermanuals") | |
| orphans = [] | |
| for md in root.rglob("*.md"): | |
| rel = md.relative_to(root) | |
| parts = rel.parts | |
| if len(parts) < 3: | |
| continue | |
| # parts = (lang, section, page.md, ...). Only the <section>/<page>.md | |
| # portion is matched against _meta.yaml. | |
| key = "/".join(parts[1:]) | |
| if key not in declared: | |
| orphans.append(str(rel)) | |
| if orphans: | |
| print("ERROR: markdown files not declared in _meta.yaml (strict orphan check):") | |
| for o in sorted(orphans): | |
| print(f" - {o}") | |
| sys.exit(1) | |
| print("OK — no orphan markdown under docs/usermanuals/.") | |
| PY |