mirror of
https://git.pumpin.online/funkpower/catena.git
synced 2026-10-22 18:57:14 +00:00
The harness compared these tools against the code they replaced — Python imported from book-pipeline, podcast-pipeline and knowledge-mcp, and JavaScript copied out of the podcast media server and indexer, each pinned to a commit. All of it has since been deleted, so the live half cannot run and the golden half pins answers against a source nobody can consult. What was worth keeping was never the comparison. `KNOWN.md` argued, at length, why a dozen of these tools deliberately do something a reader would not expect: why pdfgutter measures the crop box throughout, why avplan finds the encoder rather than reading one out of the environment, why mdscrub will not manufacture a page boundary. Those arguments are about this code and stand on their own, so they are now `docs/decisions.md`, rewritten to say what each tool does and why rather than what it does differently. Binding coverage is unaffected: the bindings carry 24 tests of their own, a node smoke test against the built package, and a typecheck.
75 lines
2.4 KiB
Python
Executable File
75 lines
2.4 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Check that every link between the markdown files goes somewhere.
|
|
|
|
Documentation that links to a page that has been renamed is worse than
|
|
documentation that does not link at all, because the reader trusts it. Run
|
|
from the repository root:
|
|
|
|
python3 tools/check-links.py
|
|
"""
|
|
|
|
import pathlib
|
|
import re
|
|
import sys
|
|
|
|
LINK = re.compile(r"\[[^\]]*\]\(([^)]+)\)")
|
|
|
|
|
|
def prose(text: str) -> str:
|
|
"""The document with its fenced code blocks taken out.
|
|
|
|
An example that happens to contain a link is an example, not a link.
|
|
"""
|
|
out, fenced = [], False
|
|
for line in text.split("\n"):
|
|
if line.startswith("```"):
|
|
fenced = not fenced
|
|
continue
|
|
if not fenced:
|
|
out.append(line)
|
|
return "\n".join(out)
|
|
|
|
|
|
def anchors(text: str) -> set[str]:
|
|
"""The headings of a document, as the fragments that reach them."""
|
|
found = set()
|
|
for line in prose(text).split("\n"):
|
|
if line.startswith("#"):
|
|
title = line.lstrip("#").strip().lower()
|
|
found.add(re.sub(r"[^a-z0-9 -]", "", title).replace(" ", "-"))
|
|
return found
|
|
|
|
|
|
def main() -> int:
|
|
root = pathlib.Path(".").resolve()
|
|
# Fixtures are not documentation: a test case is deliberately malformed
|
|
# markdown, and a link in one is part of what is being tested.
|
|
skip = {"target", "cases", "golden"}
|
|
files = sorted(
|
|
path
|
|
for path in root.rglob("*.md")
|
|
if skip.isdisjoint(path.parts) and not path.name.startswith(".")
|
|
)
|
|
headings = {path: anchors(path.read_text(encoding="utf-8")) for path in files}
|
|
|
|
broken = []
|
|
for path in files:
|
|
for target in LINK.findall(prose(path.read_text(encoding="utf-8"))):
|
|
if target.startswith(("http://", "https://", "mailto:")):
|
|
continue
|
|
where, _, fragment = target.partition("#")
|
|
dest = (path.parent / where).resolve() if where else path
|
|
if not dest.exists():
|
|
broken.append(f"{path.relative_to(root)} -> {target}: no such file")
|
|
elif fragment and dest in headings and fragment not in headings[dest]:
|
|
broken.append(f"{path.relative_to(root)} -> {target}: no such heading")
|
|
|
|
for line in broken:
|
|
print(f"broken link: {line}", file=sys.stderr)
|
|
print(f"{len(files)} files, {len(broken)} broken links")
|
|
return 1 if broken else 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|