Command line in CI
The pipeline step that fails a build when a diagram picture is stale or a generated diagram has drifted, for GitHub Actions, GitLab CI, Azure Pipelines, Jenkins, and CircleCI. Each runs the pinned release with npx, so the job installs nothing beyond Node 22.
- GitHub Actions
- GitLab
TL;DR
| Question | Answer |
|---|---|
| Which check do I run? | sync --check for the pictures under diagram fences. Add generate --check when diagrams are made from Terraform, OpenAPI or other files, and check when the repository has style rules. |
| What fails the job? | Exit code 1 for a problem in a diagram, 2 for a mistake in the command. Both fail a step in every CI system. |
| Does it need a key or a token? | No. Only check --org reads a key, from PLOTWORK_API_KEY. |
| Does it send my diagrams anywhere? | No. The check reads the checked-out files and writes nothing. |
| On GitHub, should I use this or the Action? | Either. The GitHub Action runs the same engine without Node and adds a job summary. The command line is the same step on every other CI. |
| How do I fix a failure? | Run npx -y @buun_group/plotwork@0.1.0-beta.13 sync on the same files, then commit the Markdown and the SVGs it wrote. |
Every sample pins the release. Move the pin in a change you review, and run sync in the same change, so the pictures are drawn by the release the check runs.
The commands
| Step | Command | Fails when |
|---|---|---|
| Pictures | npx -y @buun_group/plotwork@0.1.0-beta.13 sync --check "**/*.md" | A picture is missing or stale, or a diagram has a mistake |
| Generated diagrams | npx -y @buun_group/plotwork@0.1.0-beta.13 generate --check | A diagram drifted from the files it is made from |
| Style rules | npx -y @buun_group/plotwork@0.1.0-beta.13 check "**/*.md" | A rule at level error is broken |
generate --check exits 2 when the settings have no sources, so add its step only to a repository that makes diagrams from files. check exits 2 when there are no rules, in the same way.
"**/*.md" never looks in node_modules, .git, or build output. Narrow it to the folders that hold diagrams, such as README.md "docs/**/*.md", on a large repository.
generate --check --since <ref> checks only the sources whose files changed since that ref, which keeps a large repository's check short. The job needs the history up to the ref: fetch it in the checkout step.
GitHub Actions
name: diagrams
on: pull_request: paths: ['**/*.md', '**/*.diagram', 'plotwork.config.json']
permissions: contents: read
jobs: diagrams: runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 with: persist-credentials: false - uses: actions/setup-node@v6 with: node-version: 22 - run: npx -y @buun_group/plotwork@0.1.0-beta.13 sync --check --format github "**/*.md"--format github prints each stale fence and each mistake as an annotation on its line of the pull request. To report drift to code scanning, add a step that runs generate --check --format sarif --out plotwork.sarif with if: always(), then uploads the file with github/codeql-action/upload-sarif.
GitLab CI
diagrams: image: node:22 rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event" script: - npx -y @buun_group/plotwork@0.1.0-beta.13 sync --check "**/*.md" - npx -y @buun_group/plotwork@0.1.0-beta.13 generate --check --format junit --out plotwork-junit.xml artifacts: when: always reports: junit: plotwork-junit.xmlThe JUnit report shows each generated diagram as a test in the merge request, failed when it drifted.
Azure Pipelines
trigger: nonepr: branches: include: [main]
pool: vmImage: ubuntu-latest
steps: - task: NodeTool@0 inputs: versionSpec: '22.x' - script: npx -y @buun_group/plotwork@0.1.0-beta.13 sync --check "**/*.md" displayName: Diagram pictures are current - script: npx -y @buun_group/plotwork@0.1.0-beta.13 generate --check --format junit --out $(Build.ArtifactStagingDirectory)/plotwork-junit.xml displayName: Generated diagrams have not drifted - task: PublishTestResults@2 condition: always() inputs: testResultsFormat: JUnit testResultsFiles: $(Build.ArtifactStagingDirectory)/plotwork-junit.xmlJenkins
pipeline { agent { docker { image 'node:22' } } stages { stage('Diagrams') { steps { sh 'npx -y @buun_group/plotwork@0.1.0-beta.13 sync --check "**/*.md"' sh 'npx -y @buun_group/plotwork@0.1.0-beta.13 generate --check --format junit --out plotwork-junit.xml' } } } post { always { junit allowEmptyResults: true, testResults: 'plotwork-junit.xml' } }}CircleCI
version: 2.1
jobs: diagrams: docker: - image: cimg/node:22.20.0 steps: - checkout - run: npx -y @buun_group/plotwork@0.1.0-beta.13 sync --check "**/*.md" - run: mkdir -p reports && npx -y @buun_group/plotwork@0.1.0-beta.13 generate --check --format junit --out reports/plotwork-junit.xml - store_test_results: path: reports
workflows: diagrams: jobs: - diagramsOther CI systems
Any system that runs a command and reads its exit code runs the check: install Node 22, then run the command. Exit code 0 passes; 1 and 2 fail. For a runner with no access to the npm registry, mirror the package into your own registry or cache, as you would any other dependency. It is one file with no dependencies to fetch, and the checks make no request.
See also
| Doc | Why |
|---|---|
| Keep diagrams in sync in CI | The GitHub Action, its inputs, and opening a pull request with the redrawn pictures |
| Diagrams as code | Sources, overrides, and the drift check |
| Exit codes and output formats | What github, json, sarif, and junit print |
| Enterprise rollout | Reusable workflows, required checks, and air-gapped runners |