From 9c65fcb053d99ef6f5b2b36992bf144e67fb2002 Mon Sep 17 00:00:00 2001 From: Adam Moussa <166072409+amoussa1229@users.noreply.github.com> Date: Wed, 8 Jul 2026 16:20:28 -0400 Subject: [PATCH] ci: add markdown-lint and link-check CI (INFRA-128) (#16) 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. --- .github/workflows/ci.yaml | 52 +++++++++++++++++++++++++++++++++++++++ .markdownlint-cli2.jsonc | 23 +++++++++++++++++ aws-infrastructure.md | 1 + github-standards.md | 1 + lychee.toml | 20 +++++++++++++++ secrets-and-config.md | 1 + 6 files changed, 98 insertions(+) create mode 100644 .github/workflows/ci.yaml create mode 100644 .markdownlint-cli2.jsonc create mode 100644 lychee.toml diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000..026b778 --- /dev/null +++ b/.github/workflows/ci.yaml @@ -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 }} diff --git a/.markdownlint-cli2.jsonc b/.markdownlint-cli2.jsonc new file mode 100644 index 0000000..a39f4f9 --- /dev/null +++ b/.markdownlint-cli2.jsonc @@ -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"] +} diff --git a/aws-infrastructure.md b/aws-infrastructure.md index adbc4ff..b818d5f 100644 --- a/aws-infrastructure.md +++ b/aws-infrastructure.md @@ -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. When upgrading, verify on a branch first: + 1. Update `package.json` to the new version 2. Run `rm -rf node_modules package-lock.json && npm install` 3. Run `npm ci` — if it fails, the version is not safe diff --git a/github-standards.md b/github-standards.md index 4dcb0c7..5509283 100644 --- a/github-standards.md +++ b/github-standards.md @@ -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 | 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. - **`shoc-backend` / `shoc-frontend-new`** are excluded from org compliance tooling (see the `.github` org-config notes); leave their settings to the SHOC team. diff --git a/lychee.toml b/lychee.toml new file mode 100644 index 0000000..70a5594 --- /dev/null +++ b/lychee.toml @@ -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"] diff --git a/secrets-and-config.md b/secrets-and-config.md index c81a2ba..3f7503e 100644 --- a/secrets-and-config.md +++ b/secrets-and-config.md @@ -23,6 +23,7 @@ stack-name/value-name ``` Examples: + - `my-stack/slack-signing` - `my-stack/stripe-key`