diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 67a622d744..6e7c69fba9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -320,6 +320,31 @@ jobs: node-version: '22.x' cache: 'pnpm' + # `scripts/check-doc-links.mjs` existed and worked, but nothing under + # `.github/` ever called it, so it had never run in CI — it exited 1 on an + # untouched `main` for a broken link that had been sitting there (#3213, + # #3292). It resolves every `/docs/...` markdown link against + # `content/docs/` on the filesystem: no install and no network, so it runs + # here — after Node is set up, before the expensive install + site build — + # and a bad link fails in seconds instead of after a full Next.js build. + # + # KNOWN GAP — read before assuming docs links are fully gated: `ci.yml` + # lists `content/**` and `'**/*.md'` under `paths-ignore`, and GitHub has + # no per-job path filter, so a docs-ONLY pull request never starts this + # workflow and is never link-checked. This step therefore covers pull + # requests that touch docs alongside code, plus pushes to `main` — not the + # pure-docs pull request, which is the likeliest way to break a link. + # + # `control-bytes.yml` hit this same wall and answered it by being its own + # workflow with no path filters, for a gate that likewise needs no install + # and no network; its header explains the reasoning, and + # `scripts/__tests__/check-control-bytes.test.ts` pins it. Moving this + # check to that shape is the known fix, but it changes CI triggering + # policy, so it is left to the maintainer — tracked in #3448. + - name: Check docs links + if: steps.docs-changes.outputs.should_run == 'true' + run: node scripts/check-doc-links.mjs + - name: Turbo Cache if: steps.docs-changes.outputs.should_run == 'true' uses: actions/cache@v6 diff --git a/content/docs/core/enhanced-actions.mdx b/content/docs/core/enhanced-actions.mdx index a498543b37..f202ff945a 100644 --- a/content/docs/core/enhanced-actions.mdx +++ b/content/docs/core/enhanced-actions.mdx @@ -483,5 +483,5 @@ Enhanced Actions are ideal for: ## Related - [Building a CRUD App](/docs/guide/building-crud-app) - CRUD operations with actions -- [Form](/docs/components/form) - Form submission actions +- [Form](/docs/components/form/form) - Form submission actions - [Data Source](/docs/guide/data-source) - API integration diff --git a/content/docs/guide/ci-cd-pipeline.md b/content/docs/guide/ci-cd-pipeline.md index 3fc994f178..5fc89a6578 100644 --- a/content/docs/guide/ci-cd-pipeline.md +++ b/content/docs/guide/ci-cd-pipeline.md @@ -61,7 +61,7 @@ Seven jobs, all parallel — there are no `needs:` edges between them: | `test` | Test (shard N/4) | `pnpm test --shard=N/4` across a 4-runner matrix with `fail-fast: false`, so every shard reports its own failures. No coverage instrumentation — v8 adds 40–100% overhead. | **Pull requests only** | | `test-coverage` | Test (coverage) | One unsharded `pnpm test:coverage`, uploaded to Codecov. Nothing blocks on it, which is why it is not sharded. | **Push only** | | `e2e` | Build & E2E | Builds the console with `vite build` (`VITE_BASE_PATH=/console/`), verifies the artifact, then `pnpm test:e2e --project=chromium`. Uploads the Playwright report on failure. | Every run | -| `docs` | Build Docs | `turbo run build --filter='@object-ui/site'`. On a PR it first diffs against the base and skips the build when nothing under `apps/site/` or `content/` changed. | Every run (build itself conditional) | +| `docs` | Build Docs | `scripts/check-doc-links.mjs` (resolves every `/docs/...` markdown link against `content/docs/` — no install, no network), then `turbo run build --filter='@object-ui/site'`. On a PR it first diffs against the base and skips both when nothing under `apps/site/` or `content/` changed. | Every run (steps themselves conditional) | | `dev-server` | Dev-server fixture build | `pnpm --filter @object-ui/dev-server build` — guards `apps/dev-server`'s `objectstack.config.ts` against fixture / `@objectstack/spec` drift. | Every run | Uses: Node 22.x, pnpm via `corepack`, `actions/cache` over `.turbo/cache`. @@ -217,6 +217,22 @@ Backend pins live in `e2e/live/ci/backend.env` and must match the `@objectstack/ **Trigger:** Manual workflow dispatch (`workflow_dispatch`). +There are **two** link checkers, and they cover different things (objectui#3213): + +| | Covers | Network | Runs | +|---|---|---|---| +| `scripts/check-doc-links.mjs` | **Internal** `/docs/...` routes, resolved against `content/docs/` | No | In `ci.yml`'s `docs` job — see the job table above | +| Lychee (this workflow) | **External** URLs in `docs/` and `README.md` | Yes | Manual dispatch only | + +Note the asymmetry in what Lychee scans: `docs/` holds internal material (ADRs, audits, +architecture notes), while the published site is built from `content/docs/`. Lychee therefore does +not currently see the site's own pages. + +Two known gaps are tracked rather than silently lived with: `ci.yml` lists `content/**` under +`paths-ignore` and GitHub has no per-job path filter, so a **docs-only** PR does not start `ci.yml` +and is not link-checked (objectui#3448); and Lychee's scan scope predates the move to +`content/docs/` (objectui#3449). + Uses [Lychee](https://github.com/lycheeverse/lychee) with configuration from `lychee.toml`: - Scans markdown files in `docs/` and `README.md` - Max concurrency: 10, timeout: 20s, retries: 3