links #18
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # vendor-kit: links@ea0ead6abc84 — generated; edit km/packages/km-infra/vendor-kit/templates/links.yml in km, then re-sync | |
| name: links | |
| # Per-PR + weekly link checking via lychee. The cheapest fast-feedback layer | |
| # for broken links in repo docs; complements the cross-site /marketing | |
| # link-check (monthly cadence, catches bit-rot from external sites). | |
| # | |
| # Hardening (2026-06-10, 19779): GITHUB_TOKEN makes github.com checks | |
| # authenticated — the unauthenticated Monday-cron stampede across vendor | |
| # repos drew secondary rate limiting that surfaced as 504 storms (5 repos | |
| # red, zero real dead links). Cron minutes are staggered per repo via the | |
| # manifest var for the same reason. --max-retries absorbs transient | |
| # external 5xx (one-off 503s). | |
| # | |
| # Hardening (2026-07-06, 20887): some external sites (e.g. asciinema.org) | |
| # consistently time out from GitHub Actions egress IPs while resolving fine | |
| # from other networks — --max-retries can't fix a host that only stalls for | |
| # datacenter ASNs. `linksExtraExcludes` (manifest var, per-repo) is a | |
| # `--exclude` block for such CI-hostile hosts; use it instead of loosening | |
| # `--accept` or dropping the link from docs. Keep exclusions narrow (exact | |
| # host/path, not a broad prefix) so real breakage on the same site still | |
| # fails the check. (This block was live in termless since 2026-07-06 but the | |
| # template change never landed in km — recovered 2026-08-18 from the stamped | |
| # repo file when a re-sync silently deleted it.) | |
| on: | |
| pull_request: | |
| paths: ["**/*.md", "**/*.html", "docs/**"] | |
| schedule: | |
| - cron: "40 6 * * 1" # Mondays 06:xx UTC, staggered per repo | |
| workflow_dispatch: | |
| jobs: | |
| lychee: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: lycheeverse/lychee-action@v2 | |
| env: | |
| # Authenticated github.com checks — API-rate-limited instead of | |
| # anonymously throttled into 504s. | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| with: | |
| # Scoped glob: lint top-level + docs only — skips node_modules | |
| # entirely (which the workspace pulls in via bun install on CI | |
| # and pollutes the scan with thousands of npm-vendor README | |
| # links). --root-dir lets lychee parse VitePress root-relative | |
| # paths (/guide/…) without erroring; --exclude '^file:' then | |
| # skips the resulting file:// checks since VitePress validates | |
| # internal links at build time. Accept 401 for rate-limited | |
| # endpoints. CHANGELOG v0.0.1 placeholder URLs excluded. Optional | |
| # files use GLOB form ('CHANGELOG*.md') — lychee URL-interprets a | |
| # literal arg naming a missing file (http://changelog.md/ …), while | |
| # an unmatched glob is skipped. | |
| # archive.org 503s under load; the mirror is the fallback for an | |
| # already-dead original, so checking it is non-signal — ruled @cto | |
| # 2026-08-18. (A '#' inside `args: |` would reach lychee as an | |
| # argument, which is why this reason lives up here.) | |
| args: | | |
| --max-concurrency 24 | |
| --max-retries 2 | |
| --no-progress | |
| --root-dir '${{ github.workspace }}/docs' | |
| --accept '200..=204,206,301,302,303,307,308,401,403,429' | |
| --exclude '^(mailto|tel|javascript):' | |
| --exclude '^file:' | |
| --exclude '^https://web\.archive\.org/' | |
| --exclude 'github\.com/.*/(compare|releases/tag)/v0\.0\.1' | |
| README.md | |
| 'CHANGELOG*.md' | |
| 'CLAUDE*.md' | |
| 'docs/**/*.md' | |
| 'docs/**/*.html' | |
| fail: true |