Diagrams in CI/CD — Validation, Linting & Auto-Publishing in 2026

Storing diagrams as text files in Git is only the first step. Once they live alongside code, the same CI/CD discipline you apply to source — linting, validation, automated builds, deploy gates — should apply to diagrams too. Otherwise the most common failure mode is broken Mermaid blocks shipped straight into production docs because nobody previewed them.

Key Takeaway: Treat diagrams as build artifacts. Validate syntax in PR checks, render previews so reviewers can see the change, generate SVGs as part of the docs build, and run a scheduled drift watch to catch diagrams that fall out of sync with the code they describe.

The Six Stages of a Diagram Pipeline

A mature diagram pipeline mirrors a code pipeline: catch problems early, block bad changes from merging, build artifacts deterministically, and watch for drift over time. Here is the full picture:

Pipeline StagePurposeCommon Tools
Pre-commitCatch syntax errors before pushlefthook, pre-commit, mermaid-cli
PR ValidationBlock bad diagrams from mergingGitHub Actions, GitLab CI
PR PreviewRender diff visualizations in PRsreg-suit, BackstopJS, custom bots
BuildRender .mmd / .puml → SVG artifactsmermaid-cli, plantuml-jar, kroki
PublishDeploy to docs site / CDNNetlify, Vercel, Pages
Drift WatchAlert when source ↔ diagram divergeCustom workflows, scheduled jobs

Stage 1 — Pre-commit Syntax Checks

The cheapest place to catch a malformed Mermaid block is the developer's laptop, before the commit ever reaches CI. lefthook orpre-commit can run mmdc (Mermaid CLI) against staged.mmd files and reject the commit if any fail to parse.

# .pre-commit-config.yaml
repos:
  - repo: local
    hooks:
      - id: mermaid-validate
        name: Validate Mermaid diagrams
        entry: bash -c 'for f in "$@"; do mmdc -i "$f" -o /tmp/out.svg --quiet; done' --
        language: system
        files: \.mmd$

The same trick works for PlantUML (plantuml -checkonly) and D2 (d2 fmt --check).

Stage 2 — PR Validation in GitHub Actions

Pre-commit only catches contributors who installed the hook. CI is the enforcement point. A small workflow that runs on every PR can validate every diagram in the repo and block merge on failure:

# .github/workflows/diagrams.yml
name: Diagrams
on: [pull_request]

jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: '20' }
      - run: npm i -g @mermaid-js/mermaid-cli
      - name: Validate all .mmd files
        run: |
          find docs -name '*.mmd' -print0 | \
            xargs -0 -I{} mmdc -i {} -o /tmp/out.svg --quiet

Add a matrixover diagram types (Mermaid, PlantUML, D2) if your repo mixes formats. Cache the renderer's npm install for faster runs.

Stage 3 — Visual PR Previews

Reviewers should not have to mentally compile Mermaid in a code review. Two patterns fix this:

  • Auto-comment with rendered images. A workflow renders changed diagrams to PNG and posts them as a sticky PR comment. Reviewers see before/after without leaving GitHub.
  • Visual regression diffs. Tools like reg-suit compare rendered diagrams to a baseline and flag pixel diffs above a threshold. Useful for generated architecture diagrams where any change is significant.

GitHub natively renders Mermaid in Markdown, so the simplest preview is just to keep diagrams inline in .md files — the diff view shows them rendered.

Stage 4 — Build: Source → SVG

Most docs sites consume SVG, not the source DSL. Generate SVGs as a build step so the published output is deterministic and review-able. Three popular renderers:

  • Mermaid CLI (mmdc) — npm-installable, runs Puppeteer under the hood. Slow first run, fine in CI with caching.
  • PlantUML JARjava -jar plantuml.jar -tsvg src/. Fast, no JS runtime needed; pin the JAR version for reproducible builds.
  • Kroki — a single HTTP service that renders Mermaid, PlantUML, D2, BPMN, and many more. Self-host or hit kroki.io. Great when you have many formats.
# Build all diagrams in docs/diagrams/ → docs/static/
- name: Render diagrams
  run: |
    mkdir -p docs/static/diagrams
    for f in docs/diagrams/*.mmd; do
      out=docs/static/diagrams/$(basename "${f%.mmd}").svg
      mmdc -i "$f" -o "$out" --quiet
    done

Stage 5 — Auto-Publishing

Once SVGs are generated, publishing them is the same as publishing any static asset. Two patterns work well:

  • Commit-back to main — a scheduled workflow generates SVGs and commits them to a generated/ directory. Simple, but pollutes Git history.
  • Build-time generation — your docs site (Docusaurus, MkDocs, Astro) generates SVGs during its build and never commits them. Cleaner, but the docs build takes longer.

We recommend build-time generation for active repos and commit-back for low-frequency archival diagrams.

Stage 6 — Drift Detection

The hardest accessibility-of-truth problem with diagrams is drift: code changes, diagrams do not. A scheduled job can compare an auto-generated diagram (from dependency analysis, OpenAPI spec, Terraform graph) against the committed version and open an issue when they diverge.

# Weekly drift check
on:
  schedule:
    - cron: '0 9 * * 1' # Monday 09:00 UTC

jobs:
  drift:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: ./scripts/regenerate-arch-diagram.sh > /tmp/new.mmd
      - name: Compare
        run: |
          if ! diff -q docs/diagrams/architecture.mmd /tmp/new.mmd; then
            gh issue create \
              --title "Architecture diagram drift detected" \
              --body "Re-generate \`architecture.mmd\` — see workflow run."
          fi
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Format Choices Affect Pipeline Complexity

Text-based formats (Mermaid, PlantUML, D2) drop straight into this pipeline. Binary-ish formats (Excalidraw JSON, Draw.io XML) work too, but linting is harder and visual diffs require rendering both versions. If you receive Excalidraw or Draw.io files from collaborators and want to bring them into a text-based CI pipeline, convert them once with Orriguii Diagram Converter and commit the resulting.mmd or .svg as the source of truth.