Download scripts/check_consistency.py from gesh75/multivendor-cli-configurator: direct link, hf CLI and curl.
- Browser
- Download file 4.69 kB
-
https://huggingface.co/spaces/gesh75/multivendor-cli-configurator/resolve/main/scripts/check_consistency.py
- Command line
-
hf download hf://spaces/gesh75/multivendor-cli-configurator/scripts/check_consistency.py
-
curl -L -o check_consistency.py https://huggingface.co/spaces/gesh75/multivendor-cli-configurator/resolve/main/scripts/check_consistency.py
4.69 kB
| #!/usr/bin/env python3 | |
| """Fail if the docs have drifted from the real corpus in commands.json. | |
| The command count, per-role breakdown, and vendor count are repeated by hand | |
| across README.md and docs/index.html. This checker recomputes them from the | |
| single source of truth (commands.json) and asserts every derived figure still | |
| appears verbatim in those files, so a stale number fails CI instead of shipping. | |
| The README vendor table is also checked row-by-row (docs/index.html has no | |
| per-vendor counts). | |
| Design notes: | |
| - Hard failures are the raw numeric facts (total count, per-role counts, vendor | |
| count, and README per-vendor table cells) — totals/roles/vendor-count are | |
| matched as a substring so surrounding wording can change freely. | |
| - The rounded marketing form (e.g. "69,000+") and the ~MB size are advisory | |
| warnings only, since those are phrasing/rounding choices that flip on small | |
| changes and shouldn't gate a merge. | |
| Stdlib only. Run from anywhere: `python3 scripts/check_consistency.py`. | |
| Exit 0 = consistent, 1 = drift found (details printed), 2 = usage/IO error. | |
| """ | |
| from __future__ import annotations | |
| import json | |
| import re | |
| import sys | |
| from collections import Counter | |
| from pathlib import Path | |
| ROOT = Path(__file__).resolve().parent.parent | |
| CORPUS = ROOT / "commands.json" | |
| # Files that restate corpus figures. All get the same hard numeric checks. | |
| TRACKED = ("README.md", "docs/index.html") | |
| def fmt(n: int) -> str: | |
| return f"{n:,}" | |
| def rounded_thousands(n: int) -> str: | |
| return f"{(n // 1000) * 1000:,}+" | |
| def main() -> int: | |
| if not CORPUS.exists(): | |
| print(f"ERROR: {CORPUS} not found", file=sys.stderr) | |
| return 2 | |
| try: | |
| data = json.loads(CORPUS.read_text()) | |
| except (ValueError, OSError) as exc: | |
| print(f"ERROR: cannot read {CORPUS}: {exc}", file=sys.stderr) | |
| return 2 | |
| total = len(data) | |
| roles = Counter(row.get("role", "?") for row in data) | |
| vendors = sorted({row.get("vendor", "?") for row in data}) | |
| size_mb = round(CORPUS.stat().st_size / (1024 * 1024)) | |
| print("Corpus (source of truth: commands.json)") | |
| print(f" total commands : {fmt(total)}") | |
| print(f" rounded form : {rounded_thousands(total)}") | |
| print(" roles : " + " · ".join(f"{r} {fmt(c)}" for r, c in roles.most_common())) | |
| print(f" vendors : {len(vendors)}") | |
| print(f" size : ~{size_mb} MB") | |
| print() | |
| # Read each tracked file exactly once. | |
| contents: dict[str, str | None] = {} | |
| for rel in TRACKED: | |
| path = ROOT / rel | |
| contents[rel] = path.read_text() if path.exists() else None | |
| # Hard requirements: the raw numeric facts must appear verbatim. | |
| required = [fmt(total)] + [fmt(c) for c in roles.values()] + [f"{len(vendors)} vendor"] | |
| failures: list[str] = [] | |
| for rel, text in contents.items(): | |
| if text is None: | |
| failures.append(f"{rel}: file missing") | |
| continue | |
| for value in required: | |
| if value not in text: | |
| failures.append(f"{rel}: missing {value!r}") | |
| # README vendor table is the only public per-vendor count surface. | |
| # docs/index.html does not list per-vendor figures, so this is README-only. | |
| # Match the markdown row: | **Vendor** | ... | N | | |
| vendor_counts = Counter(row.get("vendor", "?") for row in data) | |
| readme = contents.get("README.md") | |
| if readme is not None: | |
| for vendor, n in sorted(vendor_counts.items(), key=lambda kv: -kv[1]): | |
| pat = rf"\| \*\*{re.escape(vendor)}\*\* \|[^|\n]+\|[^|\n]+\| {fmt(n)} \|" | |
| if not re.search(pat, readme): | |
| failures.append( | |
| f"README.md: vendor table missing {vendor} {fmt(n)}" | |
| ) | |
| # Advisory only — phrasing/rounding, not raw facts. | |
| for rel, text in contents.items(): | |
| if text is None: | |
| continue | |
| if rounded_thousands(total) not in text: | |
| print(f"WARNING: {rel} does not contain rounded form " | |
| f"{rounded_thousands(total)!r}") | |
| docs = contents.get("docs/index.html") | |
| if docs is not None and f"~{size_mb} MB" not in docs: | |
| print(f"WARNING: docs/index.html does not mention '~{size_mb} MB' " | |
| f"(commands.json is now ~{size_mb} MB)") | |
| if failures: | |
| print("\nDRIFT DETECTED — docs are out of sync with commands.json:\n") | |
| for f in failures: | |
| print(f" ✗ {f}") | |
| print("\nUpdate the file(s) above (and the profile hub) to the corpus figures.") | |
| return 1 | |
| print("OK — README.md and docs/index.html match commands.json.") | |
| return 0 | |
| if __name__ == "__main__": | |
| raise SystemExit(main()) | |