Repository navigation
Description
link-health: replace lychee with a stdlib Python link checker
Why
lychee is the only RepoMan dependency that isn't already on a typical agent image
(bash, gh, jq, curl, Python ≥ 3.8 are). On DAM it has to be installed with mise
on every VM restart, and it blocks one-click packaging as a starter kit (see the
starter-kit issue #2). It also reports egress-proxy refusals as generic errors, so under a
restricted egress preset every link to an unlisted host would be filed as "broken".
How lychee is used today (engine @ b3f128c)
- Scanner only.
link-health-scanner.shcalls it once per enrolled repo:lychee --format json --scheme http --scheme https \ --exclude localhost|127.0.0.1|localtest.me|example.com|example.org|console.cloud.google.com \ --exclude-all-private --exclude-path node_modules|vendor|.claude \ --accept 200,204,206,403,429,502,503 \ --timeout 10 --max-retries 2 --max-concurrency 8 \ [--config <repo>/.lychee.toml] <repo_dir> - lychee does link extraction (md/html/rst, skipping code blocks; pinned ≥ 0.23.0 for
that), concurrent checking with retries, and the JSON report. extract-broken-links.shreads onlytotal,errorsanderror_mapfrom that report,
normalizes status, categorizes internal/external, and suppresses.svc/.local/RFC1918
hosts (unit-tested intests/test-extract-broken-links.sh).- The fixer doesn't use lychee (curl re-verify + Wayback API).
Proposal
Add link-check.py (stdlib only: urllib, concurrent.futures, html.parser, re,
tomllib when available) that emits lychee-compatible JSON (total, errors,
error_map), so extract-broken-links.sh and its tests stay unchanged and only the one
call site in the scanner changes.
Requirements
- Extraction: markdown inline/reference/autolinks + bare URLs, HTML
href/src;
skip fenced and indented code blocks, inline code,<code>/<pre>(the old
false-positive source). - Scanner arg parity: schemes,
--excluderegexes, private-IP exclusion,
--exclude-path, accept list, timeout 10s, 2 retries, concurrency 8. -
.lychee.tomlsubset:exclude,exclude_path,accept(warn on and ignore
other keys). On Python < 3.11 (notomllib), fall back to a minimal parser for those keys. - Politeness: per-host concurrency cap, honor
Retry-Afteron 429, HEAD → GET
fallback (405/403/501), follow redirects (max 10), a real User-Agent. - Egress-refusal detection: recognize the gateway's refusal (proxy 403 / closed
connection on CONNECT) and report it with a distinct status (unchecked).
extract-broken-links.shmapsuncheckedto its own bucket, which never opens or
auto-closes an issue. Run summary + automation-health dashboard show the unchecked
count and top hosts. (This is the "unchecked" work item from the starter-kit issue Package RepoMan as a DAM starter kit (curated/repoman) #2.) - Switch:
LINK_CHECKER=python|lychee(defaultlycheeuntil cutover). - Tests: fixtures for code-block skipping, excludes, 429/Retry-After, redirect,
HEAD→GET, proxy refusal →unchecked; golden JSON compared against lychee output.
Rollout
- Land in
rossoctl/automation(engine source of truth); vendor into RepoMan with
scripts/vendor-engine.sh. - Shadow mode: run both checkers on the enrolled repos for ~2–3 weekly scans and diff
the broken sets; investigate every disagreement. - Flip the default to
python; keep lychee as an opt-in fallback for one release, then
drop it from the README prerequisites and thelink-health-scannerRequirements.
Alternative considered
Download a pinned, checksum-verified lychee release binary in the kit's install step
(GitHub is reachable under trusted). Less code to own, but keeps the external binary,
needs per-arch handling, and doesn't solve the egress-refusal classification.
Out of scope
Non-HTML/markdown formats lychee supports (rst, etc.) unless an enrolled repo needs them.
Contributions follow repo conventions: DCO sign-off + Assisted-By: trailer.
Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fieldsNew/ToDo
Activity