Files
coder/.github/workflows/weekly-docs.yaml
T
Nick Vigilante 2f34e1abd0 ci: add scheduled audit-docs-paths workflow (#27245)
## What

Adds `.github/workflows/audit-docs-paths.yaml`, a scheduled workflow
that runs the docs-URL drift audit (`site/scripts/audit-docs-paths.mjs`,
added in #25740) on a weekly cron and on demand, so drift is caught
automatically instead of only when someone runs the script by hand.
Scheduling was suggested by @bpmct on #25740.

## How it works

- **Triggers:** `schedule` (weekly, Monday 09:00 UTC — same cadence as
`weekly-docs`) and `workflow_dispatch`.
- **Checks out both repos:** `coder/coder` (root) and `coder/coder.com`
(into `coder.com/`, read with the `cdrci` CI-bot token). The audit
covers references in both repos.
- **Runs the audit** with absolute `--roots` (required, otherwise the
report can't classify findings by repo).
- **Always** uploads the dated report as the `audit-docs-paths-report`
artifact and writes it to the run summary.
- **On findings:** opens or updates a single deduplicated tracked issue
with the report, and fails the run (red check). **On a clean run:**
closes that issue.

## Enabling (dormant until then)

The audit reads **coder/coder.com, a private repo**, which the default
`GITHUB_TOKEN` can't read, so the coder.com checkout uses the existing
**`cdrci`** CI-bot token (`secrets.CDRCI_GITHUB_TOKEN`) — already used
for cross-repo checkouts in `release.yaml`/`tag-and-release.yaml`, and
`cdrci` is a coder.com collaborator (verified). No new App to stand up.
The job is gated behind `vars.AUDIT_DOCS_PATHS_ENABLED` so it merges
dormant and can be validated before going live.

To turn it on:
1. Set `vars.AUDIT_DOCS_PATHS_ENABLED = 'true'`.
2. Run once via `workflow_dispatch` to confirm the end-to-end run.

## Also in this PR

Removes the dormant `audit-docs-paths` job embedded in
`weekly-docs.yaml` (added in #25740, gated off pending the same
credential). The new dedicated workflow supersedes it; the
`weekly-docs.yaml` diff is exactly that job removal.

## Validation

- `actionlint -shellcheck= -ignore set-output` passes locally; PR
`title`, `lint-actions`, and `lint-docs` are green.
- **Credential check:** `cdrci` is a collaborator on coder/coder.com
(read access confirmed); `secrets.CDRCI_GITHUB_TOKEN` already exists in
this repo. (Note: `cdrci2` is *not* a coder.com collaborator, so an
earlier `CDRCI2_` attempt was corrected to `CDRCI_`.)
- **Pre-flight audit against current `main` (both repos): 0 findings** —
148 `/docs/*` redirect rules indexed; 1846 coder/coder + 432 coder.com
TS/TSX files scanned. So a `workflow_dispatch` on `main` today passes
green with no issue filed (the "empty audit succeeds" criterion). The
failure path can be checked by injecting a stale path on a throwaway
branch.

## Decisions for review

- **Mechanism** = tracked issue + failed check + artifact ("both" from
the issue). Easy to narrow to issue-only or fail-only.
- Reused `AUDIT_DOCS_PATHS_ENABLED` and removed the embedded job rather
than adding a second gate.
- Named the file `.yaml` to match the repo's other docs workflows (the
issue text said `.yml`).

Linear: https://linear.app/codercom/issue/DOCS-366
2026-08-10 13:40:44 -04:00

138 lines
5.5 KiB
YAML

name: weekly-docs
# runs every monday at 9 am
on:
schedule:
- cron: "0 9 * * 1"
workflow_dispatch: # allows to run manually for testing
pull_request:
branches:
- main
paths:
- "docs/**"
permissions:
contents: read
jobs:
prepare-linkspector-browser:
# later versions of Ubuntu have disabled unprivileged user namespaces, which are required by the action
runs-on: ubuntu-22.04
permissions:
contents: read
env:
CHROME_BUILD_ID: "145.0.7632.77"
outputs:
browser-cache-key: ${{ steps.browser-versions.outputs.cache-key }}
chrome-path: ${{ steps.install-chrome.outputs.path }}
steps:
- name: Harden Runner
uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0
with:
egress-policy: audit
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Set up mise tools
uses: ./.github/actions/setup-mise
with:
install-args: "node npm:@puppeteer/browsers"
- name: Get browser versions
id: browser-versions
run: |
set -euo pipefail
installer_version="$(mise current npm:@puppeteer/browsers)"
echo "cache-key=puppeteer-${RUNNER_OS}-${RUNNER_ARCH}-browsers-${installer_version}-chrome-${CHROME_BUILD_ID}" >> "$GITHUB_OUTPUT"
- name: Restore Puppeteer browser cache
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.cache/puppeteer
key: ${{ steps.browser-versions.outputs.cache-key }}
- name: Install Linkspector Chrome
id: install-chrome
run: |
set -euo pipefail
chrome_path="$(browsers install "chrome@${CHROME_BUILD_ID}" --path "${HOME}/.cache/puppeteer" --format '{{path}}')"
echo "path=${chrome_path}" >> "$GITHUB_OUTPUT"
check-docs:
needs: prepare-linkspector-browser
# later versions of Ubuntu have disabled unprivileged user namespaces, which are required by the action
runs-on: ubuntu-22.04
permissions:
pull-requests: write # required to post PR review comments by the action
steps:
- name: Harden Runner
uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0
with:
egress-policy: audit
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Rewrite same-repo links for PR branch
if: github.event_name == 'pull_request'
env:
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
run: |
# Rewrite same-repo blob/tree main links to the PR head SHA
# so that files or directories introduced in the PR are
# reachable during link checking.
{
echo 'replacementPatterns:'
echo " - pattern: \"https://github.com/coder/coder/blob/main/\""
echo " replacement: \"https://github.com/coder/coder/blob/${HEAD_SHA}/\""
echo " - pattern: \"https://github.com/coder/coder/tree/main/\""
echo " replacement: \"https://github.com/coder/coder/tree/${HEAD_SHA}/\""
} >> .github/.linkspector.yml
# TODO: Remove this workaround once action-linkspector sets
# package-manager-cache: false in its internal setup-node step.
# See: https://github.com/UmbrellaDocs/action-linkspector/issues/54
- name: Enable corepack and create pnpm store
run: |
corepack enable pnpm
mkdir -p "$(pnpm store path --silent)"
- name: Restore Puppeteer browser cache
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.cache/puppeteer
key: ${{ needs.prepare-linkspector-browser.outputs.browser-cache-key }}
- name: Check Markdown links
uses: umbrelladocs/action-linkspector@6c637d70424624231467a4ca918be54fa3b792d0 # v1.5.4
id: markdown-link-check
# checks all markdown files from /docs including all subfolders
env:
# Use the Chrome build prepared from mise-pinned Puppeteer instead
# of letting linkspector download a mutable browser at runtime.
# See: https://github.com/UmbrellaDocs/action-linkspector/issues/62
PUPPETEER_EXECUTABLE_PATH: ${{ needs.prepare-linkspector-browser.outputs.chrome-path }}
with:
# On PRs, use github-pr-review for inline comments. On schedule/dispatch,
# use local so reviewdog actually reports failures instead of silently
# exiting 0 (github-pr-review requires a PR context).
reporter: ${{ github.event_name == 'pull_request' && 'github-pr-review' || 'local' }}
config_file: ".github/.linkspector.yml"
fail_on_error: "true"
filter_mode: ${{ github.event_name == 'pull_request' && 'file' || 'nofilter' }}
- name: Send Slack notification
if: failure() && github.event_name != 'pull_request'
run: |
curl \
-X POST \
-H 'Content-type: application/json' \
-d '{"text":":warning: *Broken links found in the documentation.*\nPlease check the logs: '"${LOGS_URL}"'"}' "${{ secrets.DOCS_LINK_SLACK_WEBHOOK }}"
echo "Sent Slack notification"
env:
LOGS_URL: https://github.com/coder/coder/actions/runs/${{ github.run_id }}