CI vs Preprocessor: Choosing Your Integration Strategy
This guide helps you choose between running mdbook-lint as an mdBook preprocessor or as a standalone CLI tool in CI, and explains why you typically want one approach but not both.
TL;DR Recommendation
For CI/CD pipelines: Use the standalone CLI. For local development: Use the preprocessor (optional).
The standalone CLI gives you more control, better error handling, and avoids configuration discovery issues that can occur in preprocessor mode.
Quick Decision Guide
| Use Case | Recommended Approach | Why |
|---|---|---|
| CI/CD pipelines | Standalone CLI | More control, fail fast, better error output |
| Local development with mdBook | Preprocessor | Automatic feedback during mdbook serve |
| Pure markdown documentation (no mdBook) | Standalone CLI | No mdBook dependency needed |
| Need SARIF/GitHub integration | Standalone CLI | Better tool integration options |
| Complex CI pipeline with multiple checks | Standalone CLI | More control over when/how linting runs |
Integration Approaches
Approach 1: mdBook Preprocessor (Best for Local Development)
When to use:
- You want automatic linting during
mdbook serve - You prefer configuration in
book.toml - You want immediate feedback while writing
When NOT to use:
- In CI/CD pipelines (use standalone CLI instead)
- When you need precise control over exit codes
- When you need detailed error output for debugging
Setup in book.toml:
[preprocessor.lint]
fail-on-warnings = false # Set to true for strict mode
disabled-rules = ["MD013", "MD033"]
[MD007]
indent = 4
In CI (GitHub Actions):
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install mdBook and mdbook-lint
run: |
cargo install mdbook
cargo install mdbook-lint
- name: Build book (linting happens automatically)
run: mdbook build
env:
# Optional: Override settings for CI
MDBOOK_PREPROCESSOR__MDBOOK_LINT__FAIL_ON_WARNINGS: true
Advantages:
- Linting happens automatically during
mdbook serve - Immediate feedback while writing documentation
- Works seamlessly with local mdBook workflow
Disadvantages:
- Configuration discovery can be tricky in CI environments
- Limited control over error handling and exit codes
- No SARIF output for GitHub Security tab
- Errors appear inline with mdBook build output
- Can't fail fast in CI (must start book build first)
Approach 2: Standalone CLI (Recommended for CI/CD)
When to use:
- CI/CD pipelines (recommended for all CI use cases)
- You want to fail fast before other expensive operations
- You need clear, actionable error output
- You need SARIF output for GitHub Security integration
- You don't use mdBook (just markdown files)
Setup with .mdbook-lint.toml:
# Start existing documentation with the low-noise baseline.
preset = "baseline"
fail-on-warnings = true
Inspect the exact selection with mdbook-lint rules --preset baseline. Remove
the preset line when the project is ready to adopt the complete stable
ruleset. Project-specific exclusions can be added with disabled-rules.
In CI (GitHub Actions):
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# Option A: Using GitHub Action
- name: Lint Markdown
uses: joshrotenberg/mdbook-lint-action@v1
with:
files: 'docs/**/*.md'
format: sarif
output-file: results.sarif
# Option B: Direct installation
- name: Install and run mdbook-lint
run: |
cargo install mdbook-lint
mdbook-lint lint docs/ --preset baseline --fail-on-warnings
# Optional: Upload SARIF results
- name: Upload SARIF
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: results.sarif
SARIF output
The CLI emits SARIF v2.1.0 directly:
mdbook-lint lint docs/ --output sarif --output-file results.sarif
--output-file writes the report to disk and leaves stdout clean, so normal
diagnostics and the process exit status are unaffected. The report is written
even when the run fails, which is what a code-scanning upload step needs:
- name: Lint Markdown
run: mdbook-lint lint docs/ --output sarif --output-file results.sarif
continue-on-error: true
- name: Upload SARIF
uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: results.sarif
A clean run still produces a valid report with an empty results array, so the
upload step does not need to special-case it.
Each result carries the rule ID, a severity mapped to the SARIF levels
(error, warning, note), and a 1-based line and column. Rule descriptors are
derived from rule metadata and link to the published rule reference. Results are
sorted by location, so repeated runs over unchanged input produce identical
reports.
Advantages:
- Fails fast in CI pipeline before expensive build steps
- Clear, standalone error output for debugging
- SARIF output for GitHub Security tab
- Full control over when and how linting runs
- Can run in parallel with other checks
- Works with or without mdBook
- Supports smart CLI detection (e.g.,
mdbook-lint docs/)
Disadvantages:
- No automatic linting during local
mdbook serve - Requires explicit invocation in CI workflow
- Consider adding preprocessor for local development feedback
Why Not Both
Running mdbook-lint both as a preprocessor AND standalone in CI is usually redundant and can cause problems:
Problems with Running Both
- Duplicate Work: The same files get linted twice, wasting CI time
- Configuration Drift: Two places to maintain rules can lead to inconsistencies
- Confusing Failures: Issues might be reported twice in different formats
- Maintenance Burden: Updates need to be synchronized in multiple places
Valid Exception: Different Rule Sets
The only scenario where using both makes sense is when you intentionally want different rules:
# CI: Strict linting before build
- name: Strict lint check
run: mdbook-lint lint docs/ --config .mdbook-lint.strict.toml
# Build: Lenient linting during build
- name: Build with lenient linting
run: mdbook build # Uses preprocessor with book.toml config
Migration Strategies
From Preprocessor to Standalone CI
If you're currently using the preprocessor but want to switch to standalone CI:
- Extract configuration from
book.tomlto.mdbook-lint.toml - Remove preprocessor section from
book.toml - Update CI to run mdbook-lint before mdbook build
- Document the change for your team
From Standalone to Preprocessor
If you're using standalone but want to switch to preprocessor:
- Add preprocessor section to
book.toml - Copy configuration from
.mdbook-lint.tomltobook.toml - Remove standalone lint step from CI
- Update documentation for developers
Recommended Configurations
For CI/CD: Use Standalone CLI (Recommended)
# .github/workflows/docs.yml
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install mdbook-lint
run: cargo install mdbook-lint
- name: Lint documentation
run: mdbook-lint docs/src/ --preset baseline --fail-on-warnings
For Local Development: Add Preprocessor (Optional)
# book.toml - for local development feedback only
[preprocessor.lint]
fail-on-warnings = false
Combined Setup (Best of Both Worlds)
Use standalone CLI in CI for control and reliability, with optional preprocessor for local development:
# .github/workflows/docs.yml - CI uses standalone
- name: Lint documentation
run: mdbook-lint docs/src/ --preset baseline --fail-on-warnings
- name: Build book
run: mdbook build docs/
# book.toml - local development uses preprocessor (optional)
[preprocessor.lint]
fail-on-warnings = false
This approach gives you:
- Reliable CI with clear error output
- Fast feedback during local
mdbook serve - No duplicate configuration (use
.mdbook-lint.tomlfor both)
Common Pitfalls to Avoid
-
Don't duplicate the same rules in both preprocessor and standalone configs
-
Don't run both in CI unless you have a specific reason
-
Don't use
|| trueto ignore failures - fix the issues or disable specific rules -
Don't forget to document which approach you're using for new contributors
Summary
- Use standalone CLI in CI: Better control, clearer errors, fail-fast capability
- Use preprocessor for local development: Optional, provides feedback during
mdbook serve - Avoid using both in CI: Redundant and can cause confusion
- Share configuration: Use
.mdbook-lint.tomlwhich works for both modes - Be consistent: Document your choice and stick with it across your project