Skip to content

feat: add link-check CI workflow to prevent broken links - #8074

Open
foxxx009 wants to merge 4 commits into
Scottcjn:mainfrom
foxxx009:fix/broken-links-ci
Open

feat: add link-check CI workflow to prevent broken links#8074
foxxx009 wants to merge 4 commits into
Scottcjn:mainfrom
foxxx009:fix/broken-links-ci

Conversation

@foxxx009

Copy link
Copy Markdown

Summary This PR adds a link-check CI workflow to prevent broken links in documentation. ## Changes - Added `.github/workflows/link-check.yml` with: - Relative link validation: Checks all markdown files for broken relative links (must pass) - External link checking: Uses lychee to check external URLs (non-blocking for transient failures) - Runs on push/PR to main and weekly schedule ## Closes - Closes #7910 - Closes #7908 - Closes #7886 - Closes #7885 - Closes #7792 ## Bounty Bounty: Scottcjn/rustchain-bounties#16248 ## RTC Wallet RTC wallet address will be provided upon merge.

foxxx009 added 2 commits July 27, 2026 13:06
…-based link checker for external URLs - Add relative link validation script - Runs on push/PR to main and weekly schedule - Enforces relative links, non-blocking for external links Closes Scottcjn#7910, Scottcjn#7908, Scottcjn#7886, Scottcjn#7885, Scottcjn#7792
@foxxx009
foxxx009 requested a review from Scottcjn as a code owner July 27, 2026 05:09
@github-actions github-actions Bot added BCOS-L1 Beacon Certified Open Source tier BCOS-L1 (required for non-doc PRs) ci labels Jul 27, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Welcome to RustChain! Thanks for your first pull request.

Before we review, please make sure:

  • Non-doc PRs have a BCOS-L1 or BCOS-L2 label
  • Doc-only PRs are exempt from BCOS tier labels when they only touch docs/**, *.md, or common image/PDF files
  • New code files include an SPDX license header
  • You've tested your changes against the live node

Bounty tiers: Micro (1-10 RTC) | Standard (20-50) | Major (75-100) | Critical (100-150)

A maintainer will review your PR soon. Thanks for contributing!

@foxxx009

Copy link
Copy Markdown
Author

Done. Added SPDX license header to check_links.py. This is a doc-only PR (CI workflow + link checker script), so BCOS tier labels are not required per the policy.

…iginal find ... | while read pipe created a subshell, so 'exit 1' inside the inner loop only exited the subshell — the CI job always passed regardless of broken links. Fix: use process substitution (<()) with -print0 to avoid pipes. ERRORS counter is now checked after the loop; a non-zero count triggers 'exit 1' in the main script shell, properly failing the job.
@Scottcjn

Copy link
Copy Markdown
Owner

I ran the relative-link step from this workflow against the repo as it stands. It reports 424 broken links, and the ones I spot-checked all exist. Since that step ends in exit 1, merging this makes CI red on every PR immediately.

The checker has three bugs that account for the false positives:

1. Paths are resolved from the repo root, not from the file containing the link. resolved="$url" never applies the comment above it about handling ./ or ../. So a link from snap/README.md to ../CONTRIBUTING.md is tested as ../CONTRIBUTING.md relative to the repo root and reported broken, when CONTRIBUTING.md is right there.

2. Anchors are not stripped. CONTRIBUTING.md links to SECURITY.md#payment-authority-impersonation, and the script tests for a file literally named SECURITY.md#payment-authority-impersonation.

3. Directory links with a trailing slash. sdk/README.md links to python/, which resolves to sdk/python/ and exists.

Sample output, with the targets confirmed present in the tree:

BROKEN in ./CONTRIBUTING.md: SECURITY.md#payment-authority-impersonation   -> SECURITY.md EXISTS
BROKEN in ./snap/README.md: ../CONTRIBUTING.md                             -> CONTRIBUTING.md EXISTS
BROKEN in ./snap/README.md: ../extension/README.md                         -> extension/README.md EXISTS
BROKEN in ./sdk/README.md: python/                                         -> sdk/python EXISTS
BROKEN in ./wallet/NETWORK_ERROR_HANDLING.md: ../docs/FAQ_TROUBLESHOOTING.md -> docs/FAQ_TROUBLESHOOTING.md EXISTS
TOTAL_ERRORS=424

Two other things:

The external lychee step has both || true and continue-on-error: true, so it can never fail or even mark the job. It is doing nothing that the summary step does not already claim in text.

The repo already has .github/workflows/lychee.yml, which runs lycheeverse/lychee-action@v2 over ./**/*.md and ./**/*.html with --include-fragments and a curated exclude list, and does fail the job on breakage. That workflow already handles relative links, fragments and external links correctly, because lychee resolves paths relative to each input file. A hand-rolled bash and grep checker is not going to beat it, and check_links.py at the repo root is a third copy of the same idea with no tests and a requests dependency.

My recommendation is to close this in favour of the existing lychee.yml. If there is a gap in what lychee currently checks, a small PR adjusting that workflow's arguments would be a much better shape than a parallel checker. Thanks for digging into the link rot either way, it is a real problem in this repo.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

BCOS-L1 Beacon Certified Open Source tier BCOS-L1 (required for non-doc PRs) ci size/M PR: 51-200 lines

Projects

None yet

2 participants