Files
RustProxy/tools/checks/docs_check.py
T
2026-10-14 14:54:22 +02:00

88 lines
5.9 KiB
Python
Executable File

#!/usr/bin/env python3
"""Rust-independent documentation/Reader consistency checks for the 0.31.9 release."""
from pathlib import Path
import re
from rust_source import rust_source
ROOT = Path(__file__).resolve().parents[2]
DOCS = ROOT / "docs"
required = [
ROOT / "README.md",
ROOT / "ROADMAP.md",
ROOT / "CHANGELOG.md",
DOCS / "README.md",
DOCS / "WEB_SERVER.md",
DOCS / "TODO.md",
DOCS / "TEST_REPORT.md",
DOCS / "CHANGELOG_TECHNICAL_ARCHIVE.md",
DOCS / "ROADMAP_HISTORY.md",
]
for path in required:
assert path.is_file(), f"missing required documentation: {path.relative_to(ROOT)}"
index = (DOCS / "README.md").read_text(encoding="utf-8")
for marker in ["Functional documentation", "Development and testing", "Technical documentation", "Historical or version-labelled context", "TODO.md", "SECURITY_AUDIT_0.31.0.md", "CHANGELOG_TECHNICAL_ARCHIVE.md", "ROADMAP_HISTORY.md", "/docs"]:
assert marker in index, marker
for document in sorted(DOCS.glob("*.md")):
if document.name == "README.md":
continue
assert document.name in index, f"documentation index does not classify {document.name}"
reader_html = (ROOT / "src/admin/web/docs.html").read_text(encoding="utf-8")
reader_js = (ROOT / "src/admin/web/docs.js").read_text(encoding="utf-8")
build = (ROOT / "build.rs").read_text(encoding="utf-8")
admin = rust_source(ROOT, "src/admin/mod.rs")
for marker in ["docs-tab-reader", "docs-reader-list", "docs-reader-content", "docs-tab-api"]:
assert marker in reader_html, marker
for marker in ["renderMarkdown", "selectDocument", 'get("/docs/content.json")', "docsByPath", "docsCategoryHistorical"]:
assert marker in reader_js, marker
documentation_rs = rust_source(ROOT, "src/admin/documentation.rs") + (ROOT / "src/admin/documentation/documents.rs").read_text(encoding="utf-8")
assert "READER_DOCUMENTS" in documentation_rs and "reader_bundle" in documentation_rs
assert 'include_str!(concat!(env!("CARGO_MANIFEST_DIR")' in documentation_rs
assert 'docs-content.json' not in documentation_rs and 'documentation_bundle' not in build
for document in sorted(DOCS.glob("*.md")):
marker = f'"{document.relative_to(ROOT).as_posix()}"'
assert marker in documentation_rs, f"Reader does not embed {document.relative_to(ROOT)}"
for path in set(re.findall(r'"(docs/[A-Za-z0-9_.-]+\.md)"', documentation_rs)):
assert (ROOT / path).is_file(), f"Reader embeds missing document: {path}"
assert '"/docs/content.json"' in admin
readme = (ROOT / "README.md").read_text(encoding="utf-8")
roadmap = (ROOT / "ROADMAP.md").read_text(encoding="utf-8")
changelog = (ROOT / "CHANGELOG.md").read_text(encoding="utf-8")
assert readme.startswith("# RustProxy 0.32.7")
assert "docs/WEB_SERVER.md" in readme and "docs/README.md" in readme
assert readme.count("## What changed in ") == 1, "README duplicates historical release notes; use CHANGELOG/archive"
assert "## Release history" in readme and "docs/ROADMAP_HISTORY.md" in readme
assert roadmap.startswith("# RustProxy roadmap") and "## 0.30.0" and "## 0.29.1" in roadmap and "## 0.29.0" in roadmap and "## 0.28.2" in roadmap and "## 0.28.1" in roadmap and "## 0.27.3" in roadmap and "## 0.27.2" in roadmap and "## 0.27.1" in roadmap and "## 0.27.0" in roadmap and "## 0.26.7" in roadmap and "## 0.26.6" in roadmap and "## 0.26.5" in roadmap and "## 0.26.4" in roadmap and "## 0.26.0" in roadmap and "## 0.25.5" in roadmap and "## 0.25.4" in roadmap and "## 0.25.3" in roadmap and "## 0.25.2" in roadmap and "## 0.25.1" in roadmap and "## 0.25.0" in roadmap
assert "## 0.24." not in roadmap and "## Web Server follow-up" in roadmap and "docs/ROADMAP_HISTORY.md" in roadmap
assert changelog.startswith("# Changelog\n")
for release in ["0.29.1", "0.29.0", "0.28.2", "0.28.1", "0.28.0", "0.27.3", "0.27.2", "0.27.1", "0.27.0", "0.26.7", "0.26.6", "0.26.5", "0.26.4", "0.26.1", "0.26.0", "0.25.6", "0.25.5", "0.25.4", "0.25.3"]:
assert f"## {release}" in changelog, release
assert changelog.index("## 0.29.1") < changelog.index("## 0.29.0") < changelog.index("## 0.28.2") < changelog.index("## 0.28.1") < changelog.index("## 0.28.0") < changelog.index("## 0.27.3") < changelog.index("## 0.27.2") < changelog.index("## 0.27.1") < changelog.index("## 0.27.0") < changelog.index("## 0.26.7") < changelog.index("## 0.26.6") < changelog.index("## 0.26.5") < changelog.index("## 0.26.4") < changelog.index("## 0.26.1") < changelog.index("## 0.26.0") < changelog.index("## 0.25.6") < changelog.index("## 0.25.5")
assert "CHANGELOG_TECHNICAL_ARCHIVE.md" in changelog
for developer_only in ["Clean the remaining compiler warnings", "Update package/source/UI/OpenAPI/release identity", "source regressions and update release identity"]:
assert developer_only not in changelog, f"developer-only CHANGELOG detail: {developer_only}"
link_re = re.compile(r"\[[^\]]+\]\(([^)]+)\)")
paths = [ROOT / "README.md", ROOT / "ROADMAP.md", ROOT / "CHANGELOG.md", *sorted(DOCS.glob("*.md"))]
for source in paths:
text = source.read_text(encoding="utf-8")
assert "Run compiler-enabled `cargo fmt --all`" not in text, f"obsolete release-host checklist remains in {source.relative_to(ROOT)}"
for raw in link_re.findall(text):
target = raw.strip().split()[0].strip("<>")
if not target or target.startswith(("http://", "https://", "mailto:", "#", "/")):
continue
target = target.split("#", 1)[0].split("?", 1)[0]
if not target:
continue
resolved = (source.parent / target).resolve()
assert resolved.exists(), f"broken documentation link: {source.relative_to(ROOT)} -> {target}"
install = (DOCS / "INSTALL.md").read_text(encoding="utf-8")
for marker in ["https://rustproxy.linuxiarz.pl/", "https://rustproxy-opn.linuxiarz.pl/", "Optional: Debian 13+ one-line source install/update"]:
assert marker in install, marker
print("PASS: documentation index, Reader/API split, release docs and local Markdown links are consistent.")