# Keep diagrams in sync in CI

> Page: https://docs.plotwork.dev/diagrams-in-ci. Index of every page: https://docs.plotwork.dev/llms.txt

How to fail a pull request whose diagram pictures no longer match their source, with the GitHub Action or the command line. It is for anyone who keeps Plotwork diagrams in Markdown or `.diagram` files in a repository.

## TL;DR

| Question | Answer |
|---|---|
| What is checked? | Every `diagram` fence in the files you name: a fence with no picture under it, a picture whose source or renderer changed, and source with mistakes. `.diagram` files are checked for mistakes. |
| What runs the check on GitHub? | The action `plotwork-official/plotwork-action`, pinned by commit. The engine is bundled into it, so it installs nothing and needs no account, key, or token. |
| How does a failure show in a pull request? | Each stale fence and each mistake is an error annotation on its line in the Files changed tab, and the job summary lists the files with the command that fixes them. |
| How do I fix a failure? | Run the action with `command: sync` on the same files and commit the Markdown and the SVGs it writes, or run `npx -y @buun_group/plotwork@0.1.0-beta.13 sync` on your machine. |
| What else can the action do? | `sync` redraws in the workspace and commits nothing, `validate` reports mistakes only, `render` draws `.diagram` files to SVG, and `generate` makes diagrams from Terraform, OpenAPI, Compose and other files and fails or opens a pull request when they drift (section 5). |
| What about other CI? | The command line runs the same check: `npx -y @buun_group/plotwork@0.1.0-beta.13 sync --check "docs/**/*.md"`. See [CI usage](https://docs.plotwork.dev/cli/ci.md). |
| Does the check send my diagrams anywhere? | No. The action and the command line count nothing and, by default, make no request. Either can read an organisation's saved style rules when you name the organisation, and `generate` in write mode talks to GitHub to push and open a pull request, and to Plotwork only with `publish: true`. Both read your [repository settings](https://docs.plotwork.dev/repository-settings.md) file on the machine they run on. |

## 1. Check every pull request

Copy this workflow to `.github/workflows/plotwork.yml`:

```yaml
name: plotwork

on:
  pull_request:
    paths: ['**/*.md', '**/*.diagram']

permissions:
  contents: read

jobs:
  diagrams:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
        with:
          persist-credentials: false
      - uses: plotwork-official/plotwork-action@dcec4036897b73bd3ea724b3991f4fd0ac880757 # v1.0.0-beta.14
        with:
          paths: |
            README.md
            docs/**/*.md
```

The samples pin the latest release of the Action by its commit. `@v1` becomes available at the first stable release.

With no `paths`, the action reads every Markdown file in the repository. A line in `paths` that starts with `!` leaves out what it matches, such as `!docs/drafts/**`. Pin `actions/checkout` to a full commit SHA too, as you would any third-party action.

A stale diagram looks like this in the log, and as an error on line 12 of the file in the pull request:

```text
::error file=docs/architecture.md,line=12,title=Stale diagram::The picture under this diagram is missing or out of date.%0AFix: run plotwork-official/plotwork-action@v1.0.0-beta.14 with command: sync, then commit the Markdown file and the SVG files.
```

The job summary gives the same fix: run the action with `command: sync` and the same `paths`, then commit what it wrote. Once the command line is on npm, the summary and the annotation also give an `npx` command that pins the version built from the same engine as the action, so the pictures it draws pass the check.

## 2. The commands

| `command` | Reads | Writes | Fails when |
|---|---|---|---|
| `check` (default) | `paths` and `diagrams` | Nothing | A picture is missing or stale, or a diagram has an error |
| `sync` | `paths` | The Markdown files and their SVGs, in the workspace | A diagram has an error |
| `validate` | `paths` and `diagrams` | Nothing | A diagram has an error, or a warning with `fail-on: warning` |
| `render` | `diagrams` | A light and a dark SVG per `.diagram` file, in `out-dir` | A diagram has an error |
| `generate` | The `sources` of the settings file | Nothing with `mode: check`; with `mode: write`, the diagrams, then a pull request or a commit | A diagram drifted from its files (check mode), or a diagram has an error |

### Redraw and open a pull request

`sync` redraws the stale pictures in the runner's workspace and commits nothing; it never holds a token. To commit from a workflow, give the write permissions to the job and add a step that commits, such as `peter-evans/create-pull-request`:

```yaml
name: plotwork-sync

on:
  push:
    branches: [main]
    paths: ['**/*.md']

permissions:
  contents: read

jobs:
  sync:
    runs-on: ubuntu-latest
    permissions:
      contents: write
      pull-requests: write
    steps:
      - uses: actions/checkout@v7
        with:
          persist-credentials: false
      - id: plotwork
        uses: plotwork-official/plotwork-action@dcec4036897b73bd3ea724b3991f4fd0ac880757 # v1.0.0-beta.14
        with:
          command: sync
          paths: docs/**/*.md
      - if: steps.plotwork.outputs.stale != '0'
        uses: peter-evans/create-pull-request@v8
        with:
          branch: plotwork/sync
          commit-message: 'docs: redraw stale diagrams'
          title: 'docs: redraw stale diagrams'
          add-paths: docs/**
```

The repository must allow GitHub Actions to create pull requests (Settings, Actions, General). Most teams keep `check` on pull requests and run the sync locally.

### Report mistakes only

```yaml
- uses: plotwork-official/plotwork-action@dcec4036897b73bd3ea724b3991f4fd0ac880757 # v1.0.0-beta.14
  with:
    command: validate
    diagrams: diagrams/**/*.diagram
    fail-on: warning
```

### Draw `.diagram` files

```yaml
- uses: plotwork-official/plotwork-action@dcec4036897b73bd3ea724b3991f4fd0ac880757 # v1.0.0-beta.14
  with:
    command: render
    diagrams: diagrams/**/*.diagram
    out-dir: build/diagrams
```

`diagrams/flow.diagram` becomes `build/diagrams/diagrams/flow.svg` and `flow.dark.svg`. Each SVG carries its source, so [Editing an existing diagram](https://docs.plotwork.dev/editing-existing-diagrams.md) works on it.

## 3. Inputs and outputs

| Input | Default | Meaning |
|---|---|---|
| `command` | `check` | `check`, `sync`, `validate`, `render`, or `generate`. |
| `paths` | `**/*.md` | Markdown files: one glob, file, or folder per line. `node_modules` and `.git` are never searched. |
| `diagrams` | `**/*.diagram` | `.diagram` files, in the same form. Set it to `''` for none. |
| `out-dir` | `plotwork-svg` | Where `render` writes, inside the repository. |
| `fail-on` | `error` | `error`, `warning`, or `never`. Problems are annotated either way. |
| `convert-mermaid` | `false` | When `true`, `check` and `sync` treat each `mermaid` fence that converts as a diagram. See [Migrating from Mermaid](https://docs.plotwork.dev/migrating-from-mermaid.md). |
| `config` | empty | The [repository settings](https://docs.plotwork.dev/repository-settings.md): header defaults every command applies, and rules `check` and `validate` apply. Empty looks for `plotwork.config.json`, then `.plotwork/config.json`, from `working-directory` up to the repository root; `off` reads none. |
| `rules-org` | empty | An organisation whose saved style rules to apply instead of the file's rules, read with the key in `PLOTWORK_API_KEY`. |
| `mode`, `on-drift`, `pr-branch`, `pr-title`, `commit-message`, `publish`, `github-token` | | `generate` only. See [Diagrams as code](https://docs.plotwork.dev/diagrams-as-code.md), section 7. |
| `summary` | `true` | Write the job summary. |
| `working-directory` | `.` | The folder to run in. Annotations still name files from the repository root. |

| Output | Meaning |
|---|---|
| `stale` | Diagrams whose picture was missing or out of date. |
| `errors`, `warnings` | The problems found. |
| `files` | A JSON array: the files that need a sync (`check`), the files written (`sync`, `render`), or the files with an error (`validate`). |
| `pull-request`, `published` | `generate` only: the pull request it opened or edited, and what it published. |

`check`, `validate`, and `render` need only `contents: read`. `generate` in check mode does too; in write mode it needs `contents: write`, and `pull-requests: write` to open a pull request.

## 4. The command line

The npm package `@buun_group/plotwork` holds the bin `plotwork`, built from the same engine as the action. It runs the same check on any CI, as one command with an exit code:

```sh
npx -y @buun_group/plotwork@0.1.0-beta.13 sync --check "docs/**/*.md"
```

Pin the version, so a new release cannot change your build without a change you review. The [CLI tab](https://docs.plotwork.dev/cli/overview.md) has every command, with the pipeline for [GitLab CI, Azure Pipelines, Jenkins, and CircleCI](https://docs.plotwork.dev/cli/ci.md), the [exit codes and output formats](https://docs.plotwork.dev/cli/exit-codes-and-output.md), and the [command reference](https://docs.plotwork.dev/cli/commands.md).

## 5. Diagrams made from files

When a diagram is made from files of the repository, such as a Terraform stack or an OpenAPI document, name it in `sources` in the settings file, and let `generate` keep it in step. The full guide is [Diagrams as code](https://docs.plotwork.dev/diagrams-as-code.md).

### A pull request check

```yaml
name: diagrams

on:
  pull_request:

permissions:
  contents: read

jobs:
  drift:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
        with:
          persist-credentials: false
      - uses: plotwork-official/plotwork-action@dcec4036897b73bd3ea724b3991f4fd0ac880757 # v1.0.0-beta.14
        with:
          command: generate
```

### Regenerate on main and open a pull request

```yaml
name: diagrams-regenerate

on:
  push:
    branches: [main]
  schedule:
    - cron: '0 4 * * *'

permissions:
  contents: read

jobs:
  regenerate:
    runs-on: ubuntu-latest
    permissions:
      contents: write
      pull-requests: write
    steps:
      - uses: actions/checkout@v7
      - uses: plotwork-official/plotwork-action@dcec4036897b73bd3ea724b3991f4fd0ac880757 # v1.0.0-beta.14
        with:
          command: generate
          mode: write
```

The repository must allow GitHub Actions to create pull requests (Settings, Actions, General). A pull request opened with the workflow's own token starts no workflow; pass a GitHub App or personal token in `github-token` to have its checks run.

### Publish to an organisation

```yaml
name: diagrams-publish

on:
  push:
    branches: [main]

permissions:
  contents: read

jobs:
  publish:
    runs-on: ubuntu-latest
    permissions:
      contents: write
      pull-requests: write
    steps:
      - uses: actions/checkout@v7
      - uses: plotwork-official/plotwork-action@dcec4036897b73bd3ea724b3991f4fd0ac880757 # v1.0.0-beta.14
        with:
          command: generate
          mode: write
          publish: true
        env:
          PLOTWORK_API_KEY: ${{ secrets.PLOTWORK_API_KEY }}
```

Each source with a `publish` section, such as `{ "organization": "acme", "folder": "Platform" }`, is published with the organisation key in the secret. Only the generated diagram source is sent, never the files it was made from.

## See also

| Page | Why |
|---|---|
| [Markdown and GitHub](https://docs.plotwork.dev/markdown-and-github.md) | What a sync writes and why the pictures are committed |
| [Migrating from Mermaid](https://docs.plotwork.dev/migrating-from-mermaid.md) | Convert existing Mermaid fences first |
| [Repository settings](https://docs.plotwork.dev/repository-settings.md) | Defaults for every header, and rules a pull request must pass |
| [Diagrams as code](https://docs.plotwork.dev/diagrams-as-code.md) | Diagrams made from Terraform, OpenAPI, Compose and other files, kept in step |
| [Slack, email, Confluence, and Notion](https://docs.plotwork.dev/wikis-and-chat.md) | Share a picture outside the repository |
| [Local usage sharing](https://docs.plotwork.dev/mcp/usage.md) | What the MCP package can count, which the command line and the action never do |
