mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-08-06 19:28:06 +00:00
Verified each finding against the code; fixes for the valid ones: - install_dependencies_apt.py: the installer listed 'freetype', but the declared dependency is freetype-py — an apt miss would pip-install the wrong PyPI package. Now installs freetype-py with an import-name mapping (pre-existing bug, surfaced by the review). - api_v3.py: pixel_mapper_config is validated as a string before being saved to display.hardware (JSON callers could previously store an object/list the matrix library can't use). - .cursorrules: the Plugin Loading Process and File Organization sections still said discovery scans plugins/ — now consistent with the corrected overview (configured directory, default plugin-repos/). - README.md: removed the stale '(except the core calendar)' claim — no core calendar exists in src/ — and qualified the plugin inventory (official plugins in the monorepo; third-party from their own repos). - CONFIG_REFERENCE.md: hardware_mapping now shows the code fallback (adafruit-hat-pwm) alongside the template value. - PLUGIN_REGISTRY_SETUP_GUIDE.md: check_plugin.py takes --plugin, not a positional id. - scripts/fix_perms/fix_*.sh: exec bits set so the documented 'sudo ./...' invocations work. - Guard tests hardened: template guard now catches multi-line render_template() calls; widget guard parses actual <script> src values and fails if the widgets dir goes missing; type hints and docstrings added per repo coding guidelines. Skipped with reasons (noted on the PR): limit_refresh_rate_hz 100-vs-90 is documented as intentional in CONFIG_REFERENCE.md; the psutil comment already names the enforcing manifest; docs/archive/ findings are out of scope per the docs policy (archive may rot). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SXb4mKcAkVaxkeTb3YnAdr
44 lines
1.6 KiB
Python
44 lines
1.6 KiB
Python
"""Guard: relative markdown links in active docs must resolve.
|
|
|
|
Scans repo-root *.md and docs/ (excluding docs/archive/, which is allowed
|
|
to rot). External URLs, mailto links, and pure anchors are skipped, as are
|
|
links inside fenced code blocks.
|
|
"""
|
|
import re
|
|
from pathlib import Path
|
|
from typing import Iterator
|
|
|
|
PROJECT_ROOT = Path(__file__).resolve().parent.parent
|
|
LINK_RE = re.compile(r'\[[^\]]*\]\(([^)\s]+)\)')
|
|
FENCE_RE = re.compile(r'^(```|~~~)')
|
|
|
|
|
|
def _md_files() -> Iterator[Path]:
|
|
"""Yield active markdown files (repo root + docs/, excluding docs/archive/)."""
|
|
yield from PROJECT_ROOT.glob('*.md')
|
|
for path in PROJECT_ROOT.glob('docs/**/*.md'):
|
|
if 'archive' not in path.parts:
|
|
yield path
|
|
|
|
|
|
def test_relative_markdown_links_resolve() -> None:
|
|
"""Every relative markdown link outside code fences must resolve on disk."""
|
|
broken = []
|
|
for md in _md_files():
|
|
in_fence = False
|
|
for lineno, line in enumerate(md.read_text(encoding='utf-8').splitlines(), 1):
|
|
if FENCE_RE.match(line.strip()):
|
|
in_fence = not in_fence
|
|
continue
|
|
if in_fence:
|
|
continue
|
|
for target in LINK_RE.findall(line):
|
|
if target.startswith(('http://', 'https://', 'mailto:', '#')):
|
|
continue
|
|
resolved = (md.parent / target.split('#')[0]).resolve()
|
|
if not resolved.exists():
|
|
broken.append(
|
|
f'{md.relative_to(PROJECT_ROOT)}:{lineno} -> {target}'
|
|
)
|
|
assert not broken, 'Broken relative markdown links:\n' + '\n'.join(broken)
|