docs: add redirects.json and rebuild the docs site when it changes (#30208)

This commit is contained in:
Nick Vigilante
2026-10-02 10:30:25 -04:00
committed by GitHub
parent f96d2696a4
commit 14bb1f27d3
6 changed files with 67 additions and 28 deletions
+4 -4
View File
@@ -359,12 +359,12 @@ Don't manually edit auto-generated sections.
When renaming or moving documentation pages, redirects must be added to prevent broken links.
**Important**: Redirects are NOT configured in this repository. The coder.com website runs on Vercel with Next.js and reads redirects from a separate repository:
Redirects are configured in `docs/redirects.json` in this repository. The coder.com website runs on Vercel with Next.js, reads the file at build time, and rebuilds when it changes.
- **Redirect configuration**: https://github.com/coder/coder.com/blob/master/redirects.json
- **Redirect configuration**: `docs/redirects.json`, a JSON array of `{"source", "destination", "permanent"}` rules. Both paths are full `/docs/...` paths, `permanent` is optional and defaults to `true`, and a destination may be an external URL. A source is an exact path, a path ending in `/:path*`, or a path with a `:slug(.*)` capture.
- **Do NOT create** a `docs/_redirects` file - this format (used by Netlify/Cloudflare Pages) is not processed by coder.com
When you rename or move a doc page, create a PR in coder/coder.com to add the redirect.
When you rename or move a doc page, add the rule to `docs/redirects.json` in the same PR. Each release branch has its own copy, and the unversioned docs are served from the mainline release branch, so backport the move and its redirect together.
## Key Principles
@@ -375,4 +375,4 @@ When you rename or move a doc page, create a PR in coder/coder.com to add the re
3. **Screenshots** - Include one only when the topic needs it; refer to [Screenshot policy](#screenshot-policy)
4. **Link everything** - Related docs, API endpoints, CLI references
5. **Manifest inclusion** - Add to `docs/manifest/` for navigation
6. **Add redirects** - When moving/renaming pages, add redirects in coder/coder.com repo
6. **Add redirects** - When moving/renaming pages, add a rule to `docs/redirects.json` in the same PR
+5 -5
View File
@@ -299,13 +299,13 @@ violations it generates or suggests.
### Renames and moves require redirects
Redirects for [coder.com/docs](https://coder.com/docs) are configured in
a separate repo, not in this one. When a doc page is renamed or moved:
`docs/redirects.json` in this repo, and the website rebuilds when the file
changes. When a doc page is renamed or moved:
1. Update every link that relies on the old location.
2. Add an entry to
[`coder/coder.com:redirects.json`](https://github.com/coder/coder.com/blob/master/redirects.json)
that maps the old path to the new one. Open that PR alongside the
`coder/coder` rename PR.
2. Add a rule to `docs/redirects.json` that maps the old path to the new
one, in the same PR as the rename. For example:
`{"source": "/docs/old/path", "destination": "/docs/new/path"}`.
Do not create a `docs/_redirects` file in this repo; that format isn't
processed by coder.com.
+3 -3
View File
@@ -146,8 +146,8 @@ application notes the canonical rules do not spell out:
- On a rename, pick the new link target by the specific page each sentence
promises, not just the section hub, and confirm moved anchors still resolve.
- Keep the redirect PR in `coder/coder.com` in sync with the rename PR so the
old public path never 404s between merges.
- Add the redirect to `docs/redirects.json` in the same PR as the rename so
the old public path never 404s between merges.
## Keep PRs reviewable
@@ -198,7 +198,7 @@ the whole series as a single review.
not as conformance: tooling covers a small subset of the guide, and Vale
never fails a build. The count lives in
[What the tooling checks, and what it doesn't](../../../docs/.style/style-guide/README.md#what-the-tooling-checks-and-what-it-doesnt).
- [ ] Inbound links resolve; renames have redirects in `coder/coder.com`.
- [ ] Inbound links resolve; renames have redirects in `docs/redirects.json`.
- [ ] Premium pages carry the title suffix and manifest state.
- [ ] Series pages orient the reader and link the next step; no dead-ends.
- [ ] The change is scoped for review: large or multi-page work is split into
+42 -12
View File
@@ -24,12 +24,14 @@ name: Update coder.com/docs
# on every docs/** push.
#
# 2. vercel-rebuild: fires the Vercel deploy hook for a full
# build+deploy. Only runs when docs/manifest.json changed, since a
# manifest change can introduce or remove routes that Next.js's
# `getStaticPaths` only re-evaluates on a full rebuild.
# build+deploy. Only runs when docs/manifest.json or
# docs/redirects.json changed. A manifest change can introduce or
# remove routes that Next.js's `getStaticPaths` only re-evaluates on
# a full rebuild, and the website reads docs/redirects.json at build
# time, so a redirect only takes effect in a new build.
#
# Markdown-only edits hit only path 1 and surface in seconds. Manifest
# edits hit both paths in parallel; the ISR revalidate is harmless
# and redirects edits hit both paths in parallel; the ISR revalidate is harmless
# against the previous deployment while the new build is in flight,
# and Vercel only swaps to the new build atomically when ready.
#
@@ -92,11 +94,15 @@ concurrency:
jobs:
# Detect what changed so the dependent jobs know:
# - whether a Vercel full rebuild is needed (manifest changed), and
# - whether a Vercel full rebuild is needed (manifest or redirects
# changed), and
# - which markdown pages to surgically reindex (the changed set).
#
# Outputs:
# manifest_changed: "true" | "false"
# manifest_changed: "true" | "false"
# redirects_changed: "true" | "false". Kept separate from
# manifest_changed because it must not force the
# whole-branch reindex a manifest change does.
# paths_json: a JSON array of {path, status} objects, or "[]"
# when no markdown changes are eligible for
# surgical mode (manifest-only push, an
@@ -109,6 +115,7 @@ jobs:
runs-on: ubuntu-latest
outputs:
manifest_changed: ${{ steps.diff.outputs.manifest_changed }}
redirects_changed: ${{ steps.diff.outputs.redirects_changed }}
paths_json: ${{ steps.diff.outputs.paths_json }}
steps:
- name: Compute changed-files signal
@@ -133,6 +140,7 @@ jobs:
# resulting reindex is exhaustive.
if [ "$EVENT_NAME" != "push" ]; then
echo "manifest_changed=false" >> "$GITHUB_OUTPUT"
echo "redirects_changed=false" >> "$GITHUB_OUTPUT"
emit_whole_branch_fallback
exit 0
fi
@@ -142,6 +150,7 @@ jobs:
# whole-branch reindex.
if [ -z "${BEFORE_SHA:-}" ] || [ "$BEFORE_SHA" = "0000000000000000000000000000000000000000" ]; then
echo "manifest_changed=true" >> "$GITHUB_OUTPUT"
echo "redirects_changed=true" >> "$GITHUB_OUTPUT"
emit_whole_branch_fallback
exit 0
fi
@@ -160,9 +169,20 @@ jobs:
FIRST_ERR=$(head -1 "$GIT_ERR" 2>/dev/null || true)
echo "::warning::Could not fetch BEFORE_SHA=$BEFORE_SHA: ${FIRST_ERR:-unknown}; assuming manifest changed"
echo "manifest_changed=true" >> "$GITHUB_OUTPUT"
echo "redirects_changed=true" >> "$GITHUB_OUTPUT"
emit_whole_branch_fallback
exit 0
fi
# Redirects signal. The website reads docs/redirects.json at build
# time, so a change needs a full rebuild. Unlike the manifest it
# cannot add or remove routes, so it leaves the surgical reindex
# path below alone. Computed before the manifest signal so it is
# set even when the manifest branch exits early.
if git diff --name-only "$BEFORE_SHA" "$AFTER_SHA" -- docs/redirects.json | grep -q .; then
echo "redirects_changed=true" >> "$GITHUB_OUTPUT"
else
echo "redirects_changed=false" >> "$GITHUB_OUTPUT"
fi
# Manifest signal.
if git diff --name-only "$BEFORE_SHA" "$AFTER_SHA" -- docs/manifest.json | grep -q .; then
echo "manifest_changed=true" >> "$GITHUB_OUTPUT"
@@ -491,20 +511,30 @@ jobs:
exit "$RC"
fi
# Path 2: full Vercel rebuild. Only fires when docs/manifest.json
# changed, because manifest changes can introduce or remove routes
# that Next.js's `getStaticPaths` only re-evaluates on a full build.
# Markdown-only edits don't need this; ISR revalidate covers them.
# Path 2: full Vercel rebuild. Only fires when docs/manifest.json or
# docs/redirects.json changed. Manifest changes can introduce or
# remove routes that Next.js's `getStaticPaths` only re-evaluates on a
# full build, and redirects are read at build time. Markdown-only
# edits don't need this; ISR revalidate covers them.
vercel-rebuild:
runs-on: ubuntu-latest
needs: changes
if: needs.changes.outputs.manifest_changed == 'true'
if: needs.changes.outputs.manifest_changed == 'true' || needs.changes.outputs.redirects_changed == 'true'
steps:
- name: Trigger Vercel deploy hook
env:
HOOK: ${{ secrets.DEPLOY_DOCS_VERCEL_WEBHOOK }}
MANIFEST_CHANGED: ${{ needs.changes.outputs.manifest_changed }}
REDIRECTS_CHANGED: ${{ needs.changes.outputs.redirects_changed }}
run: |
set -euo pipefail
CHANGED_FILES=""
if [ "${MANIFEST_CHANGED:-}" = "true" ]; then
CHANGED_FILES="\`docs/manifest.json\`"
fi
if [ "${REDIRECTS_CHANGED:-}" = "true" ]; then
CHANGED_FILES="${CHANGED_FILES:+$CHANGED_FILES and }\`docs/redirects.json\`"
fi
if [ -z "${HOOK:-}" ]; then
echo "::error::DEPLOY_DOCS_VERCEL_WEBHOOK is not configured."
exit 1
@@ -537,7 +567,7 @@ jobs:
{
echo "## Vercel rebuild"
echo
echo "- Reason: \`docs/manifest.json\` changed"
echo "- Reason: ${CHANGED_FILES} changed"
echo "- HTTP status: \`${HTTP_STATUS:-n/a}\`"
echo
echo "### Response (allowlisted fields)"
+12 -4
View File
@@ -243,9 +243,17 @@ These govern *how* content enters the docs, for both humans and the doc-check ag
- **Moving or renaming a page requires link updates and a redirect.**
If a page changes its position in the directory structure:
1. Update every link that relies on its existing location.
2. Add a redirect in the
[`coder/coder.com`](https://github.com/coder/coder.com/blob/master/redirects.json)
repo (`redirects.json`).
2. Add a rule to `docs/redirects.json` in the same PR that moves the page.
Each rule has a `source` and a `destination`, both full `/docs/...` paths, and an optional `permanent` flag that defaults to `true`.
A `destination` can also be an external `http` or `https` URL.
A `source` is an exact path, a path ending in `/:path*`, or a path with a `:slug(.*)` capture.
For example: `{"source": "/docs/about/architecture", "destination": "/docs/about"}`.
The website reads `docs/redirects.json` at build time, and a change to the file starts a website rebuild.
The redirect is therefore live in the build that the merge triggers.
Each release branch has its own copy of the file, and the unversioned docs are served from the mainline release branch.
Backport the move and its redirect together so the old URL keeps working there.
Redirects that predate this file stay where they are and keep working.
Do not create a `docs/_redirects` file.
That format isn't processed by [coder.com](https://coder.com).
@@ -358,4 +366,4 @@ Update this section as they land.
If it becomes real, revisit loosening the screenshots policy.
Until then, the screenshot rules in [What belongs in the docs](#what-belongs-in-the-docs) govern.
- **doc-check redirect suggestions.**
When doc-check detects a moved or renamed page, it should suggest the exact `redirects.json` entry for `coder/coder.com` in a code block, so applying it is at most a copy-paste job.
When doc-check detects a moved or renamed page, it should suggest the exact `docs/redirects.json` entry in a code block, so applying it is at most a copy-paste job.
+1
View File
@@ -0,0 +1 @@
[]