ci/links: Add PR check for broken links - #1858
Merged
Merged
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
check-links PR check
This was referenced Sep 7, 2026
check-links PR check
marcleblanc2
added a commit
that referenced
this pull request
Sep 10, 2026
Linear [FE-499: Fix doc site issues](https://linear.app/sourcegraph/issue/FE-499/fix-doc-site-issues) 25 `#anchor` links whose target heading still exists with the same words, but the slug drifted: - `_` vs `-` - doubled `--` - a stray `"` - punctuation github-slugger strips (`ssl/https` → `sslhttps`, `merge/close` → `mergeclose`, `Sourcegraph's` → `sourcegraphs`, `kustomization.yaml` → `kustomizationyaml`) No page changes, no wording changes. Every new anchor matches a real heading on the target page, so review is a skim: the words on both sides of each `→` are the same. Checker findings (`npm run check-links -- --check-anchors`, from #1858): 280 on main → 255 on this branch. One to note: `#deploy-sourcegraph` → `#deploy-sourcegraph-` in `docs/self-hosted/deploy/machine-images/aws-oneclick.mdx` — the heading is `## Deploy Sourcegraph 🎉` and the slugger turns the emoji into a trailing `-`, so its slug has a trailing `-`. Fixing the heading instead would be cleaner but changes a URL; left as a slug fix here. ## Verification The docs site serves its not-found page with HTTP 200 (fix in progress), so status codes prove nothing. Instead, every changed link was fetched on this PR's Vercel preview and the rendered HTML checked for two `id`s: the target page's own first heading (taken from its MDX source — the 404 page never has it) and the link's `#fragment`. Script: `node dev/verify-links-live.mjs --site <preview-url>`, coming in #1858. Old links point at the current production site so you can see the breakage; new links point at the preview and land on the heading. Checked 25 changed links against https://sourcegraph-docs-git-fix-anchor-slug-drift-sourcegraph-f8c71130.vercel.app: 25 resolve, 0 fail. Page rendered = the target page's first heading id is present (the 404 page never has it); Anchor = the #fragment is an id on the page. Old links point at the current site. <details><summary>All 25 links</summary> | File containing the link | Old link (broken today) | New link (preview) | Page rendered | Anchor found | |------|-------------------------|--------------------|---------------|--------------| | `docs/admin/auth/index.mdx` | [`/admin/config/authorization-and-authentication#bitbucket-server-bitbucket-data-center-authorization`](https://sourcegraph.com/docs/admin/config/authorization-and-authentication#bitbucket-server-bitbucket-data-center-authorization) | [`/admin/config/authorization-and-authentication#bitbucket-server--bitbucket-data-center-authorization`](https://sourcegraph-docs-git-fix-anchor-slug-drift-sourcegraph-f8c71130.vercel.app/admin/config/authorization-and-authentication#bitbucket-server--bitbucket-data-center-authorization) | ✅ | ✅ | | `docs/admin/auth/saml/microsoft-adfs.mdx` | [`/self-hosted/http-https-configuration#nginx-ssl-https-configuration`](https://sourcegraph.com/docs/self-hosted/http-https-configuration#nginx-ssl-https-configuration) | [`/self-hosted/http-https-configuration#nginx-sslhttps-configuration`](https://sourcegraph-docs-git-fix-anchor-slug-drift-sourcegraph-f8c71130.vercel.app/self-hosted/http-https-configuration#nginx-sslhttps-configuration) | ✅ | ✅ | | `docs/admin/code-hosts/non-git.mdx` | [`#next-steps--advanced-configuration`](https://sourcegraph.com/docs/admin/code-hosts/non-git#next-steps--advanced-configuration) | [`#next-steps-advanced-configuration`](https://sourcegraph-docs-git-fix-anchor-slug-drift-sourcegraph-f8c71130.vercel.app/admin/code-hosts/non-git#next-steps-advanced-configuration) | ✅ | ✅ | | `docs/admin/search.mdx` | [`/self-hosted/observability/alerts#zoekt-memory-map-areas-percentage-used`](https://sourcegraph.com/docs/self-hosted/observability/alerts#zoekt-memory-map-areas-percentage-used) | [`/self-hosted/observability/alerts#zoekt-memory_map_areas_percentage_used`](https://sourcegraph-docs-git-fix-anchor-slug-drift-sourcegraph-f8c71130.vercel.app/self-hosted/observability/alerts#zoekt-memory_map_areas_percentage_used) | ✅ | ✅ | | `docs/batch-changes/batch-spec-yaml-reference.mdx` | [`#changesettemplate-branch`](https://sourcegraph.com/docs/batch-changes/batch-spec-yaml-reference#changesettemplate-branch) | [`#changesettemplatebranch`](https://sourcegraph-docs-git-fix-anchor-slug-drift-sourcegraph-f8c71130.vercel.app/batch-changes/batch-spec-yaml-reference#changesettemplatebranch) | ✅ | ✅ | | `docs/batch-changes/batch-spec-yaml-reference.mdx` | [`#steps-run`](https://sourcegraph.com/docs/batch-changes/batch-spec-yaml-reference#steps-run) | [`#stepsrun`](https://sourcegraph-docs-git-fix-anchor-slug-drift-sourcegraph-f8c71130.vercel.app/batch-changes/batch-spec-yaml-reference#stepsrun) | ✅ | ✅ | | `docs/batch-changes/batch-spec-yaml-reference.mdx` | [`#on-repository`](https://sourcegraph.com/docs/batch-changes/batch-spec-yaml-reference#on-repository) | [`#onrepository`](https://sourcegraph-docs-git-fix-anchor-slug-drift-sourcegraph-f8c71130.vercel.app/batch-changes/batch-spec-yaml-reference#onrepository) | ✅ | ✅ | | `docs/batch-changes/batch-spec-yaml-reference.mdx` | [`#changesettemplate-branch`](https://sourcegraph.com/docs/batch-changes/batch-spec-yaml-reference#changesettemplate-branch) | [`#changesettemplatebranch`](https://sourcegraph-docs-git-fix-anchor-slug-drift-sourcegraph-f8c71130.vercel.app/batch-changes/batch-spec-yaml-reference#changesettemplatebranch) | ✅ | ✅ | | `docs/batch-changes/batch-spec-yaml-reference.mdx` | [`#changesettemplate-branch`](https://sourcegraph.com/docs/batch-changes/batch-spec-yaml-reference#changesettemplate-branch) | [`#changesettemplatebranch`](https://sourcegraph-docs-git-fix-anchor-slug-drift-sourcegraph-f8c71130.vercel.app/batch-changes/batch-spec-yaml-reference#changesettemplatebranch) | ✅ | ✅ | | `docs/batch-changes/batch-spec-yaml-reference.mdx` | [`#changesettemplate-title`](https://sourcegraph.com/docs/batch-changes/batch-spec-yaml-reference#changesettemplate-title) | [`#changesettemplatetitle`](https://sourcegraph-docs-git-fix-anchor-slug-drift-sourcegraph-f8c71130.vercel.app/batch-changes/batch-spec-yaml-reference#changesettemplatetitle) | ✅ | ✅ | | `docs/batch-changes/batch-spec-yaml-reference.mdx` | [`#changesettemplate-title`](https://sourcegraph.com/docs/batch-changes/batch-spec-yaml-reference#changesettemplate-title) | [`#changesettemplatetitle`](https://sourcegraph-docs-git-fix-anchor-slug-drift-sourcegraph-f8c71130.vercel.app/batch-changes/batch-spec-yaml-reference#changesettemplatetitle) | ✅ | ✅ | | `docs/batch-changes/batch-spec-yaml-reference.mdx` | [`#workspaces-in`](https://sourcegraph.com/docs/batch-changes/batch-spec-yaml-reference#workspaces-in) | [`#workspacesin`](https://sourcegraph-docs-git-fix-anchor-slug-drift-sourcegraph-f8c71130.vercel.app/batch-changes/batch-spec-yaml-reference#workspacesin) | ✅ | ✅ | | `docs/batch-changes/batch-spec-yaml-reference.mdx` | [`#changesettemplate-branch`](https://sourcegraph.com/docs/batch-changes/batch-spec-yaml-reference#changesettemplate-branch) | [`#changesettemplatebranch`](https://sourcegraph-docs-git-fix-anchor-slug-drift-sourcegraph-f8c71130.vercel.app/batch-changes/batch-spec-yaml-reference#changesettemplatebranch) | ✅ | ✅ | | `docs/batch-changes/batch-spec-yaml-reference.mdx` | [`#steps-outputs`](https://sourcegraph.com/docs/batch-changes/batch-spec-yaml-reference#steps-outputs) | [`#stepsoutputs`](https://sourcegraph-docs-git-fix-anchor-slug-drift-sourcegraph-f8c71130.vercel.app/batch-changes/batch-spec-yaml-reference#stepsoutputs) | ✅ | ✅ | | `docs/batch-changes/site-admin-configuration.mdx` | [`/admin/config/batch-changes#automatically-delete-branches-on-merge-close`](https://sourcegraph.com/docs/admin/config/batch-changes#automatically-delete-branches-on-merge-close) | [`/admin/config/batch-changes#automatically-delete-branches-on-mergeclose`](https://sourcegraph-docs-git-fix-anchor-slug-drift-sourcegraph-f8c71130.vercel.app/admin/config/batch-changes#automatically-delete-branches-on-mergeclose) | ✅ | ✅ | | `docs/code-insights/explanations/current-limitations-of-code-insights.mdx` | [`/admin/permissions/api#Setting-sub-repository-permissions-for-users`](https://sourcegraph.com/docs/admin/permissions/api#Setting-sub-repository-permissions-for-users) | [`/admin/permissions/api#setting-sub-repository-permissions-for-users`](https://sourcegraph-docs-git-fix-anchor-slug-drift-sourcegraph-f8c71130.vercel.app/admin/permissions/api#setting-sub-repository-permissions-for-users) | ✅ | ✅ | | `docs/code-insights/references/requirements.mdx` | [`/admin/permissions/api#Setting-sub-repository-permissions-for-users`](https://sourcegraph.com/docs/admin/permissions/api#Setting-sub-repository-permissions-for-users) | [`/admin/permissions/api#setting-sub-repository-permissions-for-users`](https://sourcegraph-docs-git-fix-anchor-slug-drift-sourcegraph-f8c71130.vercel.app/admin/permissions/api#setting-sub-repository-permissions-for-users) | ✅ | ✅ | | `docs/code-navigation/auto-indexing-configuration.mdx` | [`#index-jobs`](https://sourcegraph.com/docs/code-navigation/auto-indexing-configuration#index-jobs) | [`#index_jobs`](https://sourcegraph-docs-git-fix-anchor-slug-drift-sourcegraph-f8c71130.vercel.app/code-navigation/auto-indexing-configuration#index_jobs) | ✅ | ✅ | | `docs/self-hosted/deploy/kubernetes/troubleshoot.mdx` | [`#symbols-sidebar-and-hovers-are-not-working"`](https://sourcegraph.com/docs/self-hosted/deploy/kubernetes/troubleshoot#symbols-sidebar-and-hovers-are-not-working") | [`#symbols-sidebar-and-hovers-are-not-working`](https://sourcegraph-docs-git-fix-anchor-slug-drift-sourcegraph-f8c71130.vercel.app/self-hosted/deploy/kubernetes/troubleshoot#symbols-sidebar-and-hovers-are-not-working) | ✅ | ✅ | | `docs/self-hosted/deploy/machine-images/aws-oneclick.mdx` | [`#deploy-sourcegraph`](https://sourcegraph.com/docs/self-hosted/deploy/machine-images/aws-oneclick#deploy-sourcegraph) | [`#deploy-sourcegraph-`](https://sourcegraph-docs-git-fix-anchor-slug-drift-sourcegraph-f8c71130.vercel.app/self-hosted/deploy/machine-images/aws-oneclick#deploy-sourcegraph-) | ✅ | ✅ | | `docs/self-hosted/faq.mdx` | [`/self-hosted/observability/dashboards#frontend-provisioning-container-cpu-usage-long-term`](https://sourcegraph.com/docs/self-hosted/observability/dashboards#frontend-provisioning-container-cpu-usage-long-term) | [`/self-hosted/observability/dashboards#frontend-provisioning_container_cpu_usage_long_term`](https://sourcegraph-docs-git-fix-anchor-slug-drift-sourcegraph-f8c71130.vercel.app/self-hosted/observability/dashboards#frontend-provisioning_container_cpu_usage_long_term) | ✅ | ✅ | | `docs/self-hosted/faq.mdx` | [`/self-hosted/observability/alerts#frontend-container-cpu-usage`](https://sourcegraph.com/docs/self-hosted/observability/alerts#frontend-container-cpu-usage) | [`/self-hosted/observability/alerts#frontend-container_cpu_usage`](https://sourcegraph-docs-git-fix-anchor-slug-drift-sourcegraph-f8c71130.vercel.app/self-hosted/observability/alerts#frontend-container_cpu_usage) | ✅ | ✅ | | `docs/self-hosted/faq.mdx` | [`#can-i-consume-sourcegraph-s-metrics-in-my-own-monitoring-system-datadog-new-relic-etc`](https://sourcegraph.com/docs/self-hosted/faq#can-i-consume-sourcegraph-s-metrics-in-my-own-monitoring-system-datadog-new-relic-etc) | [`#can-i-consume-sourcegraphs-metrics-in-my-own-monitoring-system-datadog-new-relic-etc`](https://sourcegraph-docs-git-fix-anchor-slug-drift-sourcegraph-f8c71130.vercel.app/self-hosted/faq#can-i-consume-sourcegraphs-metrics-in-my-own-monitoring-system-datadog-new-relic-etc) | ✅ | ✅ | | `docs/self-hosted/http-https-configuration.mdx` | [`#nginx-ssl-https-configuration`](https://sourcegraph.com/docs/self-hosted/http-https-configuration#nginx-ssl-https-configuration) | [`#nginx-sslhttps-configuration`](https://sourcegraph-docs-git-fix-anchor-slug-drift-sourcegraph-f8c71130.vercel.app/self-hosted/http-https-configuration#nginx-sslhttps-configuration) | ✅ | ✅ | | `docs/self-hosted/http-https-configuration.mdx` | [`#nginx-ssl-https-configuration`](https://sourcegraph.com/docs/self-hosted/http-https-configuration#nginx-ssl-https-configuration) | [`#nginx-sslhttps-configuration`](https://sourcegraph-docs-git-fix-anchor-slug-drift-sourcegraph-f8c71130.vercel.app/self-hosted/http-https-configuration#nginx-sslhttps-configuration) | ✅ | ✅ | </details> Dropped from this PR: 11 same-page anchors in `docs/self-hosted/deploy/kubernetes/kustomize/index.mdx`. That file is shadowed by `kustomize.mdx` at the same route and never renders, so its links cannot be verified (or clicked) on the site. They go with the shadowed-route cleanup instead. Co-authored-by: Amp <amp@ampcode.com>
marcleblanc2
enabled auto-merge (squash)
September 11, 2026 01:21
pjlast
approved these changes
Sep 11, 2026
pjlast
left a comment
Contributor
There was a problem hiding this comment.
Approving as is (it's great!), but I do wonder if there are cases where we might link to an external page that requires some sort of authorization, which could return a 404 and trigger the check unnecessarily 🤔
Contributor
Author
Those should return a 401 / 403 unauthorized instead of 404? I guess we'll find out. |
Add a pull_request workflow that runs dev/check-links.mjs --check-anchors on both the PR head and its merge base, and reports only the findings the PR introduces: outbound links from changed pages, and inbound links from other pages to a page or heading the PR removed or renamed. Pre-existing broken anchors on main are ignored. The job comments on the PR and fails when new breakage is found. dev/check-links.mjs gains --root, --format (text|json|markdown) and --baseline to support that diff, plus case-mismatch detection for routes (links that resolve on macOS but 404 on Linux) and scanning of *.md files. Only *.mdx files count as routes, matching contentlayer's filePathPattern. Amp-Thread-ID: https://ampcode.com/threads/T-01a0753f-0f4f-7478-b36c-87466e7c0261 Co-authored-by: Amp <amp@ampcode.com>
…erted) Amp-Thread-ID: https://ampcode.com/threads/T-01a0753f-0f4f-7478-b36c-87466e7c0261 Co-authored-by: Amp <amp@ampcode.com>
…l be reverted)" This reverts commit 1edcff8.
Generated pages such as admin/telemetry/protocol.mdx define anchors with <a name="..."> rather than headings. Drops 132 false positives on main (431 -> 299 with --check-anchors). Amp-Thread-ID: https://ampcode.com/threads/T-01a0753f-0f4f-7478-b36c-87466e7c0261 Co-authored-by: Amp <amp@ampcode.com>
Replace the fs.existsSync asset checks (case-insensitive on macOS, so they
hid links that 404 on the Linux build) with an enumerated lowercase -> real
path map of files under public/ and docs/, mirroring the route check.
Also register docs/index.mdx as the / route. It was never in the path map
(the /index strip needed a leading slash), and existsSync('public/') was
masking that by accepting any '/' link. Six pre-existing broken /#anchor
links on the homepage are now reported.
Amp-Thread-ID: https://ampcode.com/threads/T-01a07597-43c0-751b-8c49-6e5809e714d2
Co-authored-by: Amp <amp@ampcode.com>
A green run posts nothing. If an earlier run left a report and the PR has since been fixed, delete that comment instead of editing it to a checkmark. Amp-Thread-ID: https://ampcode.com/threads/T-01a07597-43c0-751b-8c49-6e5809e714d2 Co-authored-by: Amp <amp@ampcode.com>
The checkmark comment only ever appears after an earlier run reported breakage, so word it that way instead of "introduces no broken links". Keeps the comment (reverts the delete from the previous commit). Amp-Thread-ID: https://ampcode.com/threads/T-01a07597-43c0-751b-8c49-6e5809e714d2 Co-authored-by: Amp <amp@ampcode.com>
…-page anchors use own headings Amp-Thread-ID: https://ampcode.com/threads/T-01a07623-9d65-7356-96b8-2bebb31ffa5a Co-authored-by: Amp <amp@ampcode.com>
…ployed site Co-authored-by: Amp <amp@ampcode.com> Amp-Thread-ID: https://ampcode.com/threads/T-01a07623-9d65-7356-96b8-2bebb31ffa5a
…k, matching rehype-slug Amp-Thread-ID: https://ampcode.com/threads/T-01a07623-9d65-7356-96b8-2bebb31ffa5a Co-authored-by: Amp <amp@ampcode.com>
The checker resolves links against docs/**/*.mdx routes only and never reads src/data/redirects.ts, so a redirect added alongside a page move still leaves inbound links reported. Say so in the PR comment and in AGENTS.md so contributors update the links instead. Amp-Thread-ID: https://ampcode.com/threads/T-01a085bf-fe07-77a1-a4e2-55bff4679bcc Co-authored-by: Amp <amp@ampcode.com>
Amp-Thread-ID: https://ampcode.com/threads/T-01a088d9-b8fd-76fd-ba93-3a416c5829a3 Co-authored-by: Amp <amp@ampcode.com>
… not count Amp-Thread-ID: https://ampcode.com/threads/T-01a088d9-b8fd-76fd-ba93-3a416c5829a3 Co-authored-by: Amp <amp@ampcode.com>
… PR branch Amp-Thread-ID: https://ampcode.com/threads/T-01a088d9-b8fd-76fd-ba93-3a416c5829a3 Co-authored-by: Amp <amp@ampcode.com>
…the source view Drop glob in favour of fs.readdirSync(recursive) so the PR check needs one package instead of the whole site, and skip the pnpm/setup-node steps. Every finding now links to the file and line on the PR branch, in the ?plain=1 code view where #L anchors work. Amp-Thread-ID: https://ampcode.com/threads/T-01a088d9-b8fd-76fd-ba93-3a416c5829a3 Co-authored-by: Amp <amp@ampcode.com>
Outbound findings are in files the PR changed (it added or edited a bad link); inbound ones are elsewhere (the PR renamed or removed a link target). The workflow passes git diff --name-only against the merge base.
…l superseded runs Amp-Thread-ID: https://ampcode.com/threads/T-01a088d9-b8fd-76fd-ba93-3a416c5829a3 Co-authored-by: Amp <amp@ampcode.com>
… probe external links on added lines Absolute links to this site (https://sourcegraph.com/docs/..., the legacy docs.sourcegraph.com host, http://, //, www.) are findings even when the target exists: they leave preview deployments and local dev, and hide moved pages behind redirects. The finding proposes the relative link, following src/data/redirects.ts when the page moved. Version-pinned links (/@5.1/...) stay external. --diff replaces --changed-files and scopes two new features to added lines: --check-external requests each external link (HEAD, then GET on an error status, following redirects) and reports 404/410 only; --review writes a GitHub review with one suggested change per line, which the workflow posts, skipping suggestions already on the PR. Amp-Thread-ID: https://ampcode.com/threads/T-01a08a01-44c1-775b-84d0-d67ff9501905 Co-authored-by: Amp <amp@ampcode.com>
…he ones the suggestion leaves Amp-Thread-ID: https://ampcode.com/threads/T-01a08a01-44c1-775b-84d0-d67ff9501905 Co-authored-by: Amp <amp@ampcode.com>
… so next build fails on dead links alone Amp-Thread-ID: https://ampcode.com/threads/T-01a08a01-44c1-775b-84d0-d67ff9501905 Co-authored-by: Amp <amp@ampcode.com>
marcleblanc2
force-pushed
the
check-links-pr-check
branch
from
September 11, 2026 01:34
953692f to
492b425
Compare
marcleblanc2
added a commit
that referenced
this pull request
Sep 11, 2026
Links to this site written as https://sourcegraph.com/docs/..., http://, or https://docs.sourcegraph.com/... leave the preview deployment and local dev server, and 31 of them pointed at moved pages that only worked through src/data/redirects.ts. Found and resolved by dev/check-links.mjs on #1858; four links needed a hand fix because their anchor or page is gone. Amp-Thread-ID: https://ampcode.com/threads/T-01a08a01-44c1-775b-84d0-d67ff9501905 Co-authored-by: Amp <amp@ampcode.com>
marcleblanc2
added a commit
that referenced
this pull request
Sep 11, 2026
…#1899) ## What Rewrites every link to this site that was written as an absolute URL (`https://sourcegraph.com/docs/…`, `http://sourcegraph.com/docs/…`, `https://docs.sourcegraph.com/…`) as a relative link (`/admin/config/site-config`), in 15 files. - 32 pointed at the current route: prefix dropped. - 31 pointed at a moved page and only worked through `src/data/redirects.ts`: now link straight to the destination. - 4 needed a hand fix: the `commands#commands-id-mode` and `features#shard-merging` anchors no longer exist, `github#github-com-rate-limits` became `#rate-limits`, and the deleted gRPC guide now links to its archived 5.3 page. ## Why Absolute links leave the Vercel preview and the local dev server, and hide moved pages behind redirects. #1858 makes `dev/check-links.mjs` fail PRs that add them; this PR clears the backlog so that check starts from zero. ## Verification `node dev/check-links.mjs --check-anchors --root <this branch>` with the checker from #1858: 0 absolute self-links left, and no new findings against `main` (317 → 250 pre-existing findings; the 67 removed are all self-links). ## Notes for reviewers - Bare URLs in prose and code blocks (e.g. the `See https://docs.sourcegraph.com/admin/observability/tracing` cells in `docs/cli/references/*`) are not links and are untouched; #1851 covers those. - `docs/admin/repo/perforce.mdx`, `docs/self-hosted/observability/alerts.mdx` and `dashboards.mdx` are synced from sourcegraph/sourcegraph (#1883); the same links need the same fix upstream or the next sync reverts them. - `docs/technical-changelog.mdx` holds 31 of the links and is retired by #1888; if that lands first, take the deletion. Co-authored-by: Amp <amp@ampcode.com>
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Linear FE-499: Fix doc site issues
Problem
dev/check-links.mjsfinds broken internal links and anchors, but it isn't run automatically, so PRs can easily break links (renaming a heading, moving or deleting a page) without anyone noticingSolution
Updated the script to also work as a PR check, with additional functions beyond what's run when used as a CI test in Vercel builds
PR check to run the script and report if the PR breaks links
It runs the script (with
--check-anchors) on both the PR head and its merge base, and diffs the findingsThis catches both directions:
Outbound: a changed page links to a page or
#headingthat doesn't existInbound: the PR renames a heading or removes/moves a page that other, unchanged pages link to — those show up as findings in files the PR didn't touch
Pre-existing broken links are ignored by the PR check
The comment is created / updated in place, and once the PR is fixed, the PR check passes and the comment is updated to say so
A PR that never broke anything gets no comment
Verification
PR check comment in test PR: #1895 (comment)
Broken links found
Broken links fixed
Absolute self-links and external links
https://sourcegraph.com/docs/…,http://…,//…,www., the legacyhttps://docs.sourcegraph.com/…) fail the check even when the target exists: they leave the Vercel preview and local dev, and hide moved pages behind redirects. The finding names the relative link, followingsrc/data/redirects.tswhen the page moved. Version-pinned links (/@5.1/…) stay external*.example.com,localhost, templated<host>) are skippedTest PR: #1900 (report comment + one review suggestion; the
#samplinganchor deliberately does not exist, so that link gets no suggestion; a second run posted nothing new)Related
Amp threads