ci: add markdown-lint and link-check CI (INFRA-128) (#16)
Some checks failed
ci / ci / ci (push) Has been cancelled

Add a standalone ci workflow so handbook changes get an automated gate.
The job is named literally "ci / ci" to emit the exact status context the
org main-branch-protection ruleset requires.

- markdownlint-cli2 (.markdownlint-cli2.jsonc): MD013/MD060/MD040 relaxed
  as noisy docs-style rules; fixed 3 MD032 blank-line-around-list issues.
- lychee link check (lychee.toml): internal + external links, tolerates 429.
This commit is contained in:
Adam Moussa 2026-07-08 16:20:28 -04:00 • committed by GitHub
parent dc736da711
commit 9c65fcb053
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 98 additions and 0 deletions

52
.github/workflows/ci.yaml vendored Normal file
View file

@ -0,0 +1,52 @@
name: ci
# CI for the engineering handbook. This repo is docs-only (Markdown), so the
# gate lints Markdown and checks that links resolve.
#
# Naming is load-bearing: the org "main branch protection" ruleset matches the
# required status check against the JOB's check-run name, NOT "workflow / job".
# For a normal (non-reusable) job the check-run name IS the job name, so the job
# must be named literally "ci / ci" to emit that exact context. (A job named
# "ci" emits the context "ci" — which the PR UI cosmetically displays as
# "ci / ci" but does NOT satisfy the requirement.) This mirrors the org's
# aggregator-job convention in the `.github` repo's own self-CI.
#
# Both tools are configured to be green on the current content:
# - markdownlint-cli2 reads .markdownlint-cli2.jsonc
# - lychee reads lychee.toml (tolerates 429 so external flakiness never reds
# an otherwise-valid docs change; broken internal links still fail)
on:
pull_request:
push:
branches: [main]
permissions:
contents: read
jobs:
ci:
name: ci / ci
runs-on: ubuntu-latest
timeout-minutes: 10
concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: "24"
- name: Markdown lint
run: npx --yes markdownlint-cli2@0.23.0
- name: Link check
uses: lycheeverse/lychee-action@8646ba30535128ac92d33dfc9133794bfdd9b411 # v2.8.0
with:
args: "--config lychee.toml --no-progress './**/*.md'"
fail: true
env:
# Authenticated github.com requests avoid API rate-limit 429s.
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

23
.markdownlint-cli2.jsonc Normal file
View file

@ -0,0 +1,23 @@
{
// markdownlint-cli2 config for the engineering handbook.
// Run locally with: npx --yes markdownlint-cli2 "**/*.md" "#node_modules"
// CI runs the same command (see .github/workflows/ci.yaml).
"config": {
"default": true,
// MD013 (line-length): disabled. The handbook is prose and wide reference
// tables; hard-wrapping at 80 columns hurts readability and diffs. Line
// length is not a correctness concern for docs.
"MD013": false,
// MD060 (table-column-style): disabled. Purely cosmetic pipe-padding style
// for compact tables; the existing tables are compact and render fine.
"MD060": false,
// MD040 (fenced-code-language): disabled. Several fenced blocks are
// directory trees / plain-text output that have no meaningful language tag.
"MD040": false
},
"globs": ["**/*.md"],
"ignores": ["node_modules", ".git"]
}

View file

@ -31,6 +31,7 @@ aws-cdk-lib bundles transitive dependencies (`inBundle: true`) that npm `overrid
If a specific release is known-bad, ignore that version only (`ignore: - dependency-name: aws-cdk-lib, versions: ["2.254.0"]`) with a comment explaining why, and remove the entry once a fixed release ships. If a specific release is known-bad, ignore that version only (`ignore: - dependency-name: aws-cdk-lib, versions: ["2.254.0"]`) with a comment explaining why, and remove the entry once a fixed release ships.
When upgrading, verify on a branch first: When upgrading, verify on a branch first:
1. Update `package.json` to the new version 1. Update `package.json` to the new version
2. Run `rm -rf node_modules package-lock.json && npm install` 2. Run `rm -rf node_modules package-lock.json && npm install`
3. Run `npm ci` — if it fails, the version is not safe 3. Run `npm ci` — if it fails, the version is not safe

View file

@ -26,6 +26,7 @@ Every active repo runs the same baseline. The security half is meant to be carri
| CodeQL default setup | configured | per-repo (`PATCH .../code-scanning/default-setup state=configured`) until added to the config | | CodeQL default setup | configured | per-repo (`PATCH .../code-scanning/default-setup state=configured`) until added to the config |
Exceptions: Exceptions:
- **Docs-only repos** (e.g. `engineering-handbook`) skip CodeQL — there is no compiled code to scan; `code_security` may stay off. Secret scanning still applies. - **Docs-only repos** (e.g. `engineering-handbook`) skip CodeQL — there is no compiled code to scan; `code_security` may stay off. Secret scanning still applies.
- **`shoc-backend` / `shoc-frontend-new`** are excluded from org compliance tooling (see the `.github` org-config notes); leave their settings to the SHOC team. - **`shoc-backend` / `shoc-frontend-new`** are excluded from org compliance tooling (see the `.github` org-config notes); leave their settings to the SHOC team.

20
lychee.toml Normal file
View file

@ -0,0 +1,20 @@
# lychee link-checker config for the engineering handbook.
# Used by CI (lychee-action in .github/workflows/ci.yaml) and reproducible
# locally with:
# docker run --rm -v "$PWD:/input" lycheeverse/lychee \
# --config /input/lychee.toml /input/**/*.md
# Cache results between runs to cut repeat network calls.
cache = true
# Retry transient failures before giving up.
max_retries = 2
retry_wait_time = 2
timeout = 20
# Treat 2xx as success and tolerate 429 (rate-limited) so external flakiness
# never reds an otherwise-valid docs change. Broken internal links still fail.
accept = ["200..=299", "429"]
# Don't traverse dependency dirs.
exclude_path = ["node_modules", ".git"]

View file

@ -23,6 +23,7 @@ stack-name/value-name
``` ```
Examples: Examples:
- `my-stack/slack-signing` - `my-stack/slack-signing`
- `my-stack/stripe-key` - `my-stack/stripe-key`