From 26e66aca9bfe0bc58eb7c39ac6b01ae6acb896b4 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 25 Jul 2026 10:11:31 +0000 Subject: [PATCH] ci: add markdown internal-link checker MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This repo is documentation-heavy with dense cross-linking between README.md, docs/, and mappings/ (and more mapping-file cross-references proposed in open issues like #59). Nothing currently catches a relative link or image path broken by a future rename/move — add scripts/check_links.sh, which resolves every relative markdown link against the filesystem and fails on any that don't exist, wired into CI as a new markdown-links job. External (http/mailto) links and anchors are intentionally skipped to avoid network flakiness in CI. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01VKs7fC7VMaGgqu965dum4h --- .github/workflows/ci.yml | 10 ++++++++++ scripts/check_links.sh | 41 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100755 scripts/check_links.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9ac4e95..a3c47b1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,6 +18,16 @@ jobs: run: sudo apt-get install -y shellcheck - name: Lint customize.sh run: shellcheck scripts/customize.sh + - name: Lint check_links.sh + run: shellcheck scripts/check_links.sh + + markdown-links: + name: Markdown link check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Verify relative markdown links resolve to real files + run: bash scripts/check_links.sh placeholder-coverage: name: Placeholder coverage diff --git a/scripts/check_links.sh b/scripts/check_links.sh new file mode 100755 index 0000000..f3ecbb6 --- /dev/null +++ b/scripts/check_links.sh @@ -0,0 +1,41 @@ +#!/usr/bin/env bash +# check_links.sh — Verify that relative markdown links (and image paths) in +# every *.md file resolve to a file that actually exists in the repo. +# +# External links (http/https/mailto) and pure anchors (#section) are skipped; +# this only guards against local links rotting as files are moved or renamed. +# +# Usage: ./scripts/check_links.sh + +set -euo pipefail + +REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +cd "$REPO_ROOT" + +broken=0 + +while IFS= read -r -d '' file; do + dir="$(dirname "$file")" + + while IFS= read -r link; do + target="${link%%#*}" + [[ -z "$target" ]] && continue + + case "$target" in + http://*|https://*|mailto:*) continue ;; + esac + + resolved="$(realpath -m "${dir}/${target}" 2>/dev/null || true)" + if [[ -z "$resolved" || ! -e "$resolved" ]]; then + echo "BROKEN: $file -> $link" + broken=1 + fi + done < <(grep -oP '(?<=\]\()[^)]+(?=\))' "$file" || true) +done < <(find . -path ./.git -prune -o -name "*.md" -print0) + +if [[ $broken -ne 0 ]]; then + echo "One or more internal markdown links are broken." + exit 1 +fi + +echo "All internal markdown links resolve correctly."