---
title: CI Integration
description: Run Specdiff in CI/CD pipelines to catch breaking API changes before they merge. Includes a GitHub Actions recipe, exit code reference, and options for customizing failure thresholds and report formats.
url: https://pr-1-ee19382a0710.thally.app/specdiff/ci-integration
---

# CI Integration

Run Specdiff in CI/CD pipelines to catch breaking API changes before they merge. Includes a GitHub Actions recipe, exit code reference, and options for customizing failure thresholds and report formats.

Specdiff is designed to run in CI pipelines as a gate that blocks breaking API changes. The CLI returns structured exit codes, supports Markdown output for pull-request summaries, and provides flags for tuning which changes cause a failure.

## GitHub Actions recipe

The following workflow compares the OpenAPI spec on the pull-request branch against the base branch and fails if any breaking change is detected. The Markdown report appears in the GitHub Actions job summary.

```yaml
# .github/workflows/api-compat.yml
name: API compatibility
on: pull_request
jobs:
  specdiff:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - uses: actions/setup-node@v4
        with:
          node-version: 22
      - name: Extract the base branch spec
        run: git show origin/${{ github.base_ref }}:openapi.yaml > /tmp/openapi-base.yaml
      - name: Fail on breaking changes
        run: npx -y @specdiff/cli /tmp/openapi-base.yaml openapi.yaml --fail-on breaking --format markdown | tee -a "$GITHUB_STEP_SUMMARY"
```

Key points about this workflow:

- **`fetch-depth: 0`** is required so that `git show` can access the base branch to extract the previous version of the spec.
- **`node-version: 22`** is the minimum Node.js version Specdiff requires.
- **`git show origin/${{ github.base_ref }}:openapi.yaml`** extracts the spec file as it exists on the base branch and writes it to a temporary location, keeping the working-tree copy as the "after" document.
- **`npx -y @specdiff/cli`** runs the CLI without requiring a prior install step. The `-y` flag auto-confirms the install prompt.
- **`--format markdown`** produces a GitHub-flavored Markdown report with summary and per-severity tables.
- **`tee -a "$GITHUB_STEP_SUMMARY"`** appends the Markdown report to the job summary so it is visible directly on the Actions run page.

## Exit codes

The CLI uses distinct exit codes so your pipeline can distinguish between a detected breaking change and a configuration mistake.

| Exit code | Name | Meaning |
| --- | --- | --- |
| 0 | `ok` | No change at or above the `--fail-on` threshold. Also returned for `rules`, `explain`, `--help`, and `--version` commands. |
| 1 | `thresholdExceeded` | At least one change meets or exceeds the `--fail-on` threshold. |
| 2 | `usage` | Usage error: unknown flag, invalid value, or unknown rule code passed to `--ignore-rule`. |
| 3 | `inputError` | An input document could not be read or parsed. |

In a CI pipeline, exit code 1 is the expected failure mode when the API has an incompatible change. Exit codes 2 and 3 indicate problems with the pipeline configuration or input files and should be investigated separately.

## Failure thresholds with --fail-on

The `--fail-on` flag controls which severity level triggers exit code 1.

| Value | Behavior |
| --- | --- |
| `breaking` (default) | Fail only when at least one breaking change exists. |
| `warning` | Fail when at least one breaking or warning change exists. |
| `info` | Fail on any change at all, including informational ones. |
| `none` | Never fail. Always exit 0 regardless of changes. Useful for advisory reports. |

For example, to treat warnings as failures:

```sh
npx specdiff old.yaml new.yaml --fail-on warning
```

To produce a report without blocking the pipeline:

```sh
npx specdiff old.yaml new.yaml --fail-on none --format markdown
```

## Markdown output for PR comments

Use `--format markdown` to produce a report suitable for pull-request comments or job summaries. The output includes a heading, a summary table with change counts, and per-severity detail tables listing each change with its rule code, JSON-pointer path, and message.

To write the report directly to the GitHub Actions job summary:

```sh
npx -y @specdiff/cli base.yaml head.yaml --format markdown | tee -a "$GITHUB_STEP_SUMMARY"
```

To write the report to a file for use with a later step that posts a PR comment:

```sh
npx -y @specdiff/cli base.yaml head.yaml --format markdown --fail-on none -o report.md
```

For machine-readable output that you can feed into other tools, use `--format json` instead.

## Suppressing known changes

When a breaking change is intentional or already tracked, you can suppress it so it does not block the pipeline.

### Ignoring specific rules

Use `--ignore-rule` to drop all changes produced by a given rule. The flag is repeatable.

```sh
npx specdiff old.yaml new.yaml \
  --ignore-rule description-changed \
  --ignore-rule deprecated-added
```

If you pass an unknown rule code, the CLI exits with code 2 (usage error).

### Ignoring paths

Use `--ignore-path` to drop all changes at or beneath a JSON-pointer prefix. The flag is repeatable. The leading `#` is optional.

```sh
npx specdiff old.yaml new.yaml \
  --ignore-path "#/paths/~1internal" \
  --ignore-path "#/paths/~1admin"
```

This silences every change under the `/internal` and `/admin` endpoints, which is useful when those paths are not part of the public contract.

### Combining suppression flags

Both flags can be used together. Ignored rules and ignored paths are applied before the threshold check, so suppressed changes do not count toward the `--fail-on` decision.

```sh
npx specdiff old.yaml new.yaml \
  --fail-on breaking \
  --ignore-rule description-changed \
  --ignore-path "#/paths/~1internal"
```