linkcheck
Fails a build when a relative link in a repository's Markdown does not resolve.
$ linkcheck -root .
docs/design/ADR-011.md:42: broken link -> ../srd/SRD-999-missing.md
exit status 1
Install
go install github.com/dr-dobermann/linkcheck/cmd/linkcheck@latest
Why it exists
Documentation links rot silently. Nothing fails when a doc is renamed, so the
breakage is discovered by a reader, months later, one link at a time. A single
sweep of one repository turned up 78 dead cross-references accumulated
across several refactors — including its README and its top-level architecture
document.
It is a small Go program rather than an off-the-shelf checker because a CI gate
should be pinned, offline and installable with go install. Alternatives add a
non-Go toolchain plus a network dependency that reddens the gate for reasons
unrelated to the change under test.
What it checks
Every relative link target in every .md file under -root, including
reference-style definitions ([id]: path). A target that does not resolve to
an existing file is reported as file:line.
What it deliberately ignores
Both exclusions are requirements learned from real false positives, not
conveniences:
- Fenced and inline code. A Go generic signature such as
`values.NewArray[T](vals…)` is indistinguishable from a Markdown link
to a naive regex. One repository had eight such spans.
- Absolute and protocol-relative URLs,
mailto:, and same-document anchors.
They rot for reasons outside the repository, and verifying them would make
the gate depend on the network.
Targets are percent-decoded before resolution, or every correct link to a
file whose name contains a space reports as broken. Bracketed targets
([text](<path with spaces>)) and titles ([text](path "title")) are handled.
Exit codes
| Code |
Meaning |
| 0 |
every relative link resolves |
| 1 |
at least one link is broken (each printed as file:line) |
| 2 |
usage error — an unreadable root, a bad flag |
License
LGPL-3.0, the same as the project it was extracted from.