Files
2026-07-13 13:12:33 +08:00

71 lines
2.4 KiB
Python

from __future__ import annotations
import re
from pathlib import Path
_ROOT = Path(__file__).parent.parent
# Pattern to find shell script references in fenced code blocks
_FENCE_RE = re.compile(r"```[^\n]*\n(.*?)```", re.DOTALL)
_SCRIPT_REF_RE = re.compile(
r"(?:^|\s)(?:bash|sh|pwsh|powershell)\s+([\w./\\-]+\.(?:sh|ps1))"
r"|(?:^|\s)\.([\w./\\-]+\.(?:sh|ps1))"
r"|(?:^|\s)\.\/([\w./\\-]+\.(?:sh|ps1))",
re.MULTILINE,
)
_PORTABLE_DISCLAIMER_RE = re.compile(
r"portable zip|Portable Zip|Available after the first GitHub Release",
re.IGNORECASE,
)
def _get_readme_lines() -> list[str]:
return (_ROOT / "README.md").read_text(encoding="utf-8").splitlines()
def _surrounding_lines(all_lines: list[str], line_idx: int, radius: int = 2) -> str:
start = max(0, line_idx - radius)
end = min(len(all_lines), line_idx + radius + 1)
return "\n".join(all_lines[start:end])
def test_readme_script_references_exist_or_have_portable_disclaimer() -> None:
readme_text = (_ROOT / "README.md").read_text(encoding="utf-8")
all_lines = readme_text.splitlines()
violations: list[str] = []
for fence_match in _FENCE_RE.finditer(readme_text):
block_content = fence_match.group(1)
block_start_pos = fence_match.start(1)
for ref_match in _SCRIPT_REF_RE.finditer(block_content):
# Extract whichever capture group matched
script_name = ref_match.group(1) or ref_match.group(2) or ref_match.group(3)
if script_name is None:
continue
# Normalize path separators and strip leading ./
script_name = script_name.lstrip("./").lstrip(".\\")
# Absolute position in the readme
abs_pos = block_start_pos + ref_match.start()
line_idx = readme_text[:abs_pos].count("\n")
# Check if file exists at repo root
if (_ROOT / script_name).exists():
continue
# Check surrounding lines for portable disclaimer
context = _surrounding_lines(all_lines, line_idx, radius=2)
if _PORTABLE_DISCLAIMER_RE.search(context):
continue
violations.append(
f"Line {line_idx + 1}: '{script_name}' not found at repo root "
f"and no portable-zip disclaimer in surrounding 2 lines"
)
assert violations == [], "\n".join(violations)