Skip to content

Commit 464d319

Browse files
committed
ci: add a markdown link + badge checker for README and docs/
The README and docs/ carry many links, shields.io badges, and raw-GitHub asset URLs. Dead links and dead badges are a common rot in public repos and there was no guard, so a moved file or renamed asset would silently 404 for visitors. Add a links workflow that runs lychee over README.md and docs/**/*.md: - Triggers on PRs that touch a *.md file (plus the checker's own config) and on a weekly cron, with workflow_dispatch for manual runs. - Hard 404s fail the job. Flaky or non-fetchable hosts are tolerated via .lycheeignore: localhost examples, shields.io / hub.docker.com / star-history badge hosts, and the interactive "ask an assistant" deep links (chatgpt.com / claude.ai / gemini.google.com) that block bots. Real content links (github.com, raw.githubusercontent.com, npm, pypi, the detector sites) stay checked. - Uses GITHUB_TOKEN so the many github.com links don't hit the anonymous API rate limit. Document the workflow and the .lycheeignore escape hatch in CONTRIBUTING.md. Closes #13
1 parent cd7ffda commit 464d319

3 files changed

Lines changed: 60 additions & 0 deletions

File tree

.github/workflows/links.yml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
name: links
2+
3+
# Dead links and dead badges are a common rot in public repos. This verifies every
4+
# markdown link + image/badge URL in README.md and docs/ still resolves, so a moved
5+
# file or renamed release asset can't silently 404 for visitors.
6+
on:
7+
pull_request:
8+
paths:
9+
- "**/*.md"
10+
- ".lycheeignore"
11+
- ".github/workflows/links.yml"
12+
schedule:
13+
- cron: "0 6 * * 1" # Mondays 06:00 UTC — catches external rot between PRs
14+
workflow_dispatch:
15+
16+
permissions:
17+
contents: read
18+
19+
concurrency:
20+
group: links-${{ github.ref }}
21+
cancel-in-progress: true
22+
23+
jobs:
24+
linkcheck:
25+
name: markdown links + badges
26+
runs-on: ubuntu-latest
27+
steps:
28+
- uses: actions/checkout@v4
29+
- name: Check links
30+
uses: lycheeverse/lychee-action@v2
31+
with:
32+
# Hard 404s fail the job; flaky/rate-limited hosts are tolerated via .lycheeignore.
33+
args: --no-progress --max-retries 3 README.md "docs/**/*.md"
34+
fail: true
35+
env:
36+
# Authenticated github.com requests avoid the low anonymous API rate limit.
37+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.lycheeignore

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Hosts lychee should not check. One regex per line, matched against the whole URL.
2+
# Keep this list to genuinely-flaky or non-fetchable endpoints — real content links
3+
# (github.com, raw.githubusercontent.com, npm, pypi, detector sites) stay checked so
4+
# a moved file or renamed asset is caught.
5+
6+
# Local dev endpoints that appear in code examples, not real links.
7+
^https?://localhost
8+
^https?://127\.0\.0\.1
9+
10+
# Badge / image hosts that rate-limit CI crawlers; a stale badge is cosmetic, not a 404.
11+
img\.shields\.io
12+
hub\.docker\.com
13+
star-history\.com
14+
15+
# "Ask an assistant" deep links — interactive endpoints that block bots or require login.
16+
^https://chatgpt\.com
17+
^https://claude\.ai
18+
^https://gemini\.google\.com

CONTRIBUTING.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,11 @@ Optionally install the git hooks so they run automatically:
5959
pip install pre-commit && pre-commit install
6060
```
6161

62+
Markdown links and badges in `README.md` and `docs/` are verified by the **links** workflow
63+
([lychee](https://github.com/lycheeverse/lychee-action)) — on PRs that touch a `*.md` file and on
64+
a weekly schedule. A hard 404 fails the job. If an external host merely rate-limits the CI crawler,
65+
add it to [`.lycheeignore`](.lycheeignore) rather than leaving the check red.
66+
6267
## Submitting a change
6368

6469
1. **Open an issue first** for anything beyond a typo, so we can agree on the surface and approach.

0 commit comments

Comments
 (0)