39 lines
1.3 KiB
Python
39 lines
1.3 KiB
Python
import re, sys, os
|
|
|
|
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
DOCS = os.path.join(ROOT, "docs")
|
|
|
|
link_re = re.compile(r'\[[^\]]*\]\(([^)]+)\)')
|
|
|
|
errors = []
|
|
checked = 0
|
|
|
|
for dirpath, _, filenames in os.walk(DOCS):
|
|
for fn in filenames:
|
|
if not fn.endswith(".md"):
|
|
continue
|
|
path = os.path.join(dirpath, fn)
|
|
with open(path, encoding="utf-8") as f:
|
|
content = f.read()
|
|
for m in link_re.finditer(content):
|
|
target = m.group(1).strip()
|
|
if target.startswith(("http://", "https://", "mailto:")):
|
|
continue
|
|
# strip fragment
|
|
target_path = target.split("#", 1)[0]
|
|
if not target_path:
|
|
continue
|
|
resolved = os.path.normpath(os.path.join(dirpath, target_path))
|
|
checked += 1
|
|
if not os.path.exists(resolved):
|
|
errors.append(f"{os.path.relpath(path, ROOT)}: broken link -> {target} (resolved: {os.path.relpath(resolved, ROOT)})")
|
|
|
|
print(f"Checked {checked} relative links across docs/*.md")
|
|
if errors:
|
|
print(f"\n{len(errors)} BROKEN LINK(S):")
|
|
for e in errors:
|
|
print(" " + e)
|
|
sys.exit(1)
|
|
else:
|
|
print("All relative links resolve to existing files.")
|