Skip to content
Open
92 changes: 92 additions & 0 deletions .github/workflows/check-links.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
name: Check links

# Reports internal links and #anchors that this PR breaks, compared with the
# merge base. Pre-existing broken links on the base branch are ignored.

on:
pull_request:

permissions:
contents: read
pull-requests: write

jobs:
check-links:
name: Broken links introduced by this PR
runs-on: ubuntu-latest
steps:
- name: Check out pull request head
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 0

- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 10.25.0

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: 20.19.6
cache: pnpm

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Check out merge base
env:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
run: git worktree add "$RUNNER_TEMP/base" "$(git merge-base "$BASE_SHA" HEAD)"

- name: Record broken links already present on the base branch
# Exit 1 means findings, which is expected here
run: |
node dev/check-links.mjs --check-anchors --format json \
--root "$RUNNER_TEMP/base" > "$RUNNER_TEMP/base-links.json" \
|| [ $? -eq 1 ]

- name: Find broken links introduced by this PR
id: check
run: |
if node dev/check-links.mjs --check-anchors --format markdown \
--baseline "$RUNNER_TEMP/base-links.json" > "$RUNNER_TEMP/report.md"; then
echo "broken=false" >> "$GITHUB_OUTPUT"
else
echo "broken=true" >> "$GITHUB_OUTPUT"
fi
cat "$RUNNER_TEMP/report.md"

- name: Comment on the pull request
# Fork PRs get a read-only token; the report is still in the job log
if: github.event.pull_request.head.repo.full_name == github.repository
env:
GH_TOKEN: ${{ github.token }}
PR_NUMBER: ${{ github.event.pull_request.number }}
BROKEN: ${{ steps.check.outputs.broken }}
run: |
marker='<!-- check-links-report -->'
existing_comment=$(gh api "repos/$GITHUB_REPOSITORY/issues/$PR_NUMBER/comments" \
--paginate --jq ".[] | select(.body | startswith(\"$marker\")) | .id" | head -n 1)

# Comment only when there is something to report, or an earlier report to resolve
if [ "$BROKEN" = true ]; then
{ echo "$marker"; cat "$RUNNER_TEMP/report.md"; } > "$RUNNER_TEMP/comment.md"
elif [ -n "$existing_comment" ]; then
printf '%s\n### ✅ The broken links an earlier revision of this PR introduced are fixed\n' \
"$marker" > "$RUNNER_TEMP/comment.md"
else
exit 0
fi

if [ -n "$existing_comment" ]; then
gh api --method PATCH "repos/$GITHUB_REPOSITORY/issues/comments/$existing_comment" \
--field body=@"$RUNNER_TEMP/comment.md"
else
gh pr comment "$PR_NUMBER" --body-file "$RUNNER_TEMP/comment.md"
fi

- name: Fail when this PR introduces broken links
if: steps.check.outputs.broken == 'true'
run: exit 1
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
- **Build**: `npm run build`
- **Dev**: `npm run dev`
- **Lint**: `npm run lint`
- **Check links**: `npm run check-links -- --check-anchors` (CI comments on PRs that break links; see `dev/check-links.mjs`)

## AI Chat Integration

Expand Down
Loading
Loading