CmdocsCmdocs
Deployment

CI Validation

Run cmdocs check locally and in CI so broken docs never reach main.

CI Validation

cmdocs check is a fast, deterministic lint that catches the mistakes most likely to break your docs site. Run it locally before committing, wire it into a pre-commit hook, and run it in CI on every pull request — broken docs should never survive the path to main.

What it checks

CheckExample failure
docs.json schemaname is required, Expected hex color (#RRGGBB), unknown field
Duplicate tab pathsTwo tabs both resolve to the same URL segment — hard error
Duplicate page filesThe same MDX file listed under two groups
Navigation → MDXMissing MDX: guides/setup.mdx (referenced in "Documentation" > "Guides")
MDX frontmatterMissing frontmatter "title" in: quickstart.mdx
AssetsMissing logo: public/logo/dark.svg
Internal linksBroken link in index.mdx:42: "/documentation/missing-page"

Exit code 0 means you're clean. Exit code 1 prints every failure with a file and line number.

The three places to run it

  1. Locally while authoring — fastest feedback loop.
  2. Pre-commit hook — prevents broken commits from ever reaching the remote.
  3. CI on pull requests — blocks merges even if someone skipped the pre-commit hook.

Run it locally

npx @cmdoss/cmdocs check

Point at a specific source directory if your docs aren't at the repo root:

npx @cmdoss/cmdocs check --source ./docs

Sample output

All checks pass:

ℹ Checking docs.json...
✓ Loaded "My Documentation"
ℹ Checking navigation references...
ℹ Checking MDX frontmatter...
ℹ Checking assets...
ℹ Checking internal links...
✓ All checks passed

Broken link caught:

ℹ Checking internal links...
✖ Broken link in guides/writing-content.mdx:42: "/documentation/missing-page"
✖ 1 error found

Pre-commit hook

Block bad commits before they happen. Pick whichever style fits your project.

Zero dependencies — just a shell script. Create .git/hooks/pre-commit:

#!/usr/bin/env bash
npx @cmdoss/cmdocs check

Make it executable:

chmod +x .git/hooks/pre-commit

.git/hooks/ isn't tracked by git. For hooks that travel with the repo, use a folder like .githooks/ and point git at it:

git config core.hooksPath .githooks

If your project already uses Husky, add cmdocs check to the pre-commit hook:

npx husky init
echo 'npx @cmdoss/cmdocs check' >> .husky/pre-commit

Commit .husky/pre-commit — every contributor who runs npm install gets the hook automatically.

For large repos where you only want to validate when docs-related files changed, combine with lint-staged:

package.json
{
  "lint-staged": {
    "**/*.{mdx,json}": "npx @cmdoss/cmdocs check"
  }
}

Then in .husky/pre-commit:

npx lint-staged

cmdocs check typically runs in well under a second. Keep it in pre-commit — it's faster than running a type-checker and catches a different class of errors.

Pre-push hook (alternative)

Some teams prefer to validate only at push time, not on every commit. Create .git/hooks/pre-push (or .githooks/pre-push):

#!/usr/bin/env bash
npx @cmdoss/cmdocs check

Pre-commit gives faster feedback; pre-push is lighter-weight when authors make many small commits.

CI on pull requests

GitHub Actions

.github/workflows/docs.yml
name: Docs

on:
  pull_request:
    paths:
      - "**/*.mdx"
      - "docs.json"

jobs:
  check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: "20"
      - run: npx @cmdoss/cmdocs check

For a monorepo with docs under apps/docs/:

      - run: npx @cmdoss/cmdocs check --source apps/docs

GitLab CI

.gitlab-ci.yml
docs-check:
  image: node:20
  script:
    - npx @cmdoss/cmdocs check
  rules:
    - changes:
        - "**/*.mdx"
        - "docs.json"

Other CI providers

The pattern is identical on any CI platform: install Node 20, run npx @cmdoss/cmdocs check, let the exit code gate the merge.

What happens if I skip it?

The cmdocs dashboard's build worker runs the same validation before building — if your docs.json is broken or pages are missing, the dashboard build fails and the deployment never goes live. CI just catches this earlier, before it consumes dashboard build minutes and before a broken commit sits on main.

Troubleshooting

What's next

How is this guide?

On this page