Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions .github/workflows/links.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: links

# Dead links and dead badges are a common rot in public repos. This verifies every
# markdown link + image/badge URL in README.md and docs/ still resolves, so a moved
# file or renamed release asset can't silently 404 for visitors.
on:
pull_request:
paths:
- "**/*.md"
- ".lycheeignore"
- ".github/workflows/links.yml"
schedule:
- cron: "0 6 * * 1" # Mondays 06:00 UTC — catches external rot between PRs
workflow_dispatch:

permissions:
contents: read

concurrency:
group: links-${{ github.ref }}
cancel-in-progress: true

jobs:
linkcheck:
name: markdown links + badges
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Check links
uses: lycheeverse/lychee-action@v2
with:
# Hard 404s fail the job; flaky/rate-limited hosts are tolerated via .lycheeignore.
args: --no-progress --max-retries 3 README.md "docs/**/*.md"
fail: true
env:
# Authenticated github.com requests avoid the low anonymous API rate limit.
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
22 changes: 22 additions & 0 deletions .lycheeignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Hosts lychee should not check. One regex per line, matched against the whole URL.
# Keep this list to genuinely-flaky or non-fetchable endpoints — real content links
# (github.com, raw.githubusercontent.com, npm, pypi, detector sites) stay checked so
# a moved file or renamed asset is caught.

# Local dev endpoints that appear in code examples, not real links.
^https?://localhost
^https?://127\.0\.0\.1

# Badge / image hosts that rate-limit CI crawlers; a stale badge is cosmetic, not a 404.
img\.shields\.io
hub\.docker\.com
star-history\.com

# npm's registry returns 403 Forbidden to non-browser crawlers (bot protection), so the
# package page is not fetchable from CI. PyPI, by contrast, is checked normally.
npmjs\.com

# "Ask an assistant" deep links — interactive endpoints that block bots or require login.
^https://chatgpt\.com
^https://claude\.ai
^https://gemini\.google\.com
5 changes: 5 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@ Optionally install the git hooks so they run automatically:
pip install pre-commit && pre-commit install
```

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

## Submitting a change

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