diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ace17f0d3..e2f26b0ea 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1395,6 +1395,26 @@ jobs: if: steps.sccache_probe.outputs.active == 'true' run: sccache --show-stats + docs-links: + name: Markdown links resolve (lychee) + runs-on: ubuntu-latest + timeout-minutes: 20 + # Deliberately NOT gated by the `changes` path filter, for the same reason + # as architecture-doc above: a markdown link can go stale when the file or + # heading it points at moves, without the markdown file itself changing. + # Run unconditionally instead. + # + # A manual E2E dispatch only exercises the E2E jobs; skip the rest. + if: github.event_name != 'workflow_dispatch' + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + + - name: Check markdown links + uses: lycheeverse/lychee-action@e7477775783ea5526144ba13e8db5eec57747ce8 # v2.9.0 + with: + args: --config lychee.toml './**/*.md' + fail: true + docs-build: name: Sphinx docs build (-W) runs-on: ubuntu-latest diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9d5f2f7ec..7d9ed0f7f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -79,6 +79,8 @@ Crate-layering invariants (e.g. `rocmd` must never depend on `rocm`) are enforce `docs/architecture.md`'s path citations are enforced by `cargo xtask check-architecture-doc` (`xtask/src/architecture_doc.rs`) — it fails CI if a citation isn't found where it's cited, naming the expected location per citation (exactly where depends on its shape: a slash path, a bare filename, or a bare directory name; see `citation_exists`'s doc comment in that file for the full rule) — though it doesn't check the accuracy of the surrounding prose. +Every local (relative-path) markdown link and `#anchor` fragment in the repo — outside `docs/rocm-docs/`, which `docs-build` covers instead — is checked by the `docs-links` CI job (`lychee.toml`); it runs offline only, so it doesn't catch broken external `https://` links (see `lychee.toml` for the current exclusions). + ### Test commands | Component | Command | diff --git a/lychee.toml b/lychee.toml new file mode 100644 index 000000000..5779d56e8 --- /dev/null +++ b/lychee.toml @@ -0,0 +1,30 @@ +# Config for the `docs-links` CI job (.github/workflows/ci.yml), which checks +# every markdown link in the repo resolves. First step toward replacing +# xtask/src/architecture_doc.rs's hand-rolled path-citation checker: that +# checker stays in place, running alongside this gate, until a follow-up PR +# rewrites docs/architecture.md's citations as markdown links and removes it +# — see issue #578 for why. + +# Only check local file/anchor links, no network requests. Keeps this gate +# immune to external-host flakiness (rate limits, transient DNS) — see the +# "Offline-only" tradeoff recorded in issue #578. +offline = true + +# Check that `#anchor` fragments resolve too, not just the file path. +include_fragments = "anchor-only" + +# GitHub's private-vulnerability-reporting links in SECURITY.md +# (../../security/advisories/new) are relative-looking but meant to resolve +# on GitHub's web UI, not as a local file — offline mode can't follow them. +exclude = [ + 'security/advisories/new', +] + +# docs/rocm-docs/ is Sphinx/MyST source, not plain markdown: its cross-doc +# anchors (e.g. `commands.md#model-serving`) are generated at Sphinx build +# time from `{include}` directives lychee doesn't evaluate, so it reports +# false positives there. That tree's correctness is already covered by the +# `docs-build` (Sphinx -W) CI job, which does understand MyST. +exclude_path = [ + '^docs/rocm-docs/', +]