Metadata-Version: 2.4
Name: specshift
Version: 1.1.0
Summary: Detects, classifies, and optionally summarizes breaking changes in OpenAPI/Swagger and GraphQL contracts using AI
Author: Lethe044
License: MIT
Project-URL: Homepage, https://github.com/Lethe044/specshift
Project-URL: Repository, https://github.com/Lethe044/specshift
Project-URL: Issues, https://github.com/Lethe044/specshift/issues
Project-URL: Changelog, https://github.com/Lethe044/specshift/blob/main/CHANGELOG.md
Keywords: openapi,swagger,graphql,api,breaking-changes,contract-testing,api-diff,devtools,cli,ci-cd,github-actions
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Testing
Classifier: Topic :: Software Development :: Quality Assurance
Classifier: Topic :: Internet :: WWW/HTTP
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: pyyaml>=6.0
Requires-Dist: requests>=2.28
Provides-Extra: pretty
Requires-Dist: rich>=13.0; extra == "pretty"
Provides-Extra: dev
Requires-Dist: pytest>=7.4; extra == "dev"
Requires-Dist: pytest-cov>=4.1; extra == "dev"
Requires-Dist: responses>=0.23; extra == "dev"
Requires-Dist: rich>=13.0; extra == "dev"
Dynamic: license-file

# SpecShift

[![CI](https://github.com/Lethe044/specshift/actions/workflows/ci.yml/badge.svg)](https://github.com/Lethe044/specshift/actions/workflows/ci.yml)
[![PyPI version](https://img.shields.io/pypi/v/specshift.svg)](https://pypi.org/project/specshift/)
[![Python versions](https://img.shields.io/pypi/pyversions/specshift.svg)](https://pypi.org/project/specshift/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)

SpecShift detects changes in OpenAPI and Swagger contracts, classifies each
one as breaking, warning, or info, and can optionally summarize them in
plain language.

When an API changes from one version to the next, the real question isn't
"what changed" but "will this break me". SpecShift is built to answer
exactly that: it takes two specifications, evaluates every difference
between them individually, and tells you which ones actually matter.

```
$ specshift diff examples/old_api.yaml examples/new_api.yaml

Bookstore API : 1.0.0 -> 2.0.0
13 breaking, 2 warning, 3 info changes found.

[BREAKING] DELETE /books/{bookId} :: HTTP method removed
[BREAKING] GET /books > parameter 'category' :: Parameter 'category' is now required
[BREAKING] GET /books > response 200 > field 'author' :: field removed from response
...

Result: 13 breaking change(s) make this update risky.
```

## Why SpecShift

Every team that keeps evolving its API eventually hits the same problem:
a field gets removed, a parameter becomes required, an enum value
disappears, and nobody notices until a client breaks in production.
Most existing diff tools just show you a raw JSON diff and leave it up to
you to figure out what actually matters.

SpecShift doesn't do that. It evaluates every change based on its context:

- Removing a field from a **response** is **breaking**, because clients
  may depend on it being there.
- Removing the same field from a **request body** is usually just a
  **warning**, because clients that send it are simply ignored, not broken.
- Adding a new required field to a request is **breaking**, but adding a
  new field to a response is just **informational**.

These context-aware rules are the core of SpecShift, and they work
completely free, with no API key required. The optional AI-powered
natural-language summary is an additional layer on top, never a
requirement.

## Features

- **Comprehensive structural diff**: deep comparison at the path, HTTP
  method, parameter, request body, response, and schema level.
- **GraphQL support**: diff GraphQL SDL schemas with `specshift diff-graphql`,
  covering types, fields, arguments, enum values, interfaces, unions, and
  deprecations, no external GraphQL library required.
- **Context-aware classification**: the same change is weighted
  differently depending on whether it occurs in a request or a response
  (or, for GraphQL, an input type versus an object type).
- **`$ref` resolution and `allOf` merging**: correctly follows the
  reference and composition patterns common in real-world specifications.
- **Detects enum, format, nullable, security scheme, content-type,
  `additionalProperties`, `readOnly`/`writeOnly`, validation constraint,
  default value, and `oneOf`/`anyOf` changes**.
- **Works entirely for free**: no API key or paid service is required.
- **Optional AI summary**: can generate a natural-language summary using
  Groq, the Gemini free tier, or any OpenAI-compatible endpoint. If no key
  is set, it automatically falls back to a rule-based summary and never
  stops working.
- **CI/CD integration**: the `specshift check` command compares the
  current specification against a branch and fails the build if a
  breaking change is found. An official GitHub Action is also available
  for one-line setup with automatic PR comments (see below).
- **Live monitoring**: the `specshift watch` command periodically checks
  a remote API's specification and sends a Slack or Discord notification
  when it changes.
- **Four output formats**: a colored console table, a Markdown report
  (ideal for PR comments), JSON (for integrating with other tools), and a
  standalone, filterable HTML report you can open in any browser.

## Installation

```bash
pip install specshift
```

For colored console output (optional, works fine without it too):

```bash
pip install "specshift[pretty]"
```

Installing from source:

```bash
git clone https://github.com/Lethe044/specshift.git
cd specshift
pip install -e .
```

## Quick start

Compare two specifications directly:

```bash
specshift diff old_openapi.yaml new_openapi.yaml
```

You can also compare specifications from URLs:

```bash
specshift diff https://api.example.com/v1/openapi.json https://api.example.com/v2/openapi.json
```

To use it in CI, create a configuration file in your repo:

```bash
specshift init
```

This produces a `.specshift.yml` file similar to:

```yaml
spec_path: openapi.yaml
base_ref: main
fail_on: breaking
```

Then, in your CI pipeline:

```bash
specshift check
```

This command compares the current `openapi.yaml` file against its version
on the `main` branch and returns exit code 1 if a breaking change is found.

## AI summary (optional)

SpecShift can use free-tier AI services to generate a natural-language
summary of the changes. This never requires any payment:

```bash
export GROQ_API_KEY="your-groq-api-key"
specshift diff old.yaml new.yaml --ai
```

You can also use Google Gemini's free tier instead of Groq:

```bash
export GEMINI_API_KEY="your-gemini-api-key"
specshift diff old.yaml new.yaml --ai --ai-provider gemini
```

If you want to use a more powerful (paid) model, you can connect any
OpenAI-compatible endpoint:

```bash
export SPECSHIFT_API_KEY="your-api-key"
export SPECSHIFT_OPENAI_BASE_URL="https://api.openai.com/v1"
specshift diff old.yaml new.yaml --ai --ai-provider openai_compatible --ai-model gpt-4o-mini
```

If no key is configured, the `--ai` flag still works, it simply produces a
rule-based summary instead of waiting on a network call. AI support is an
optional enhancement, never a requirement.

## Commands

### `specshift diff <old> <new>`

Compares two specifications. `<old>` and `<new>` can be a file path, an
http(s) URL, or raw JSON/YAML text.

Useful options:

| Option | Description |
|---|---|
| `--format console\|markdown\|json\|html` | Output format (default: console) |
| `--output <file>` | Writes the output to a file |
| `--ai` | Adds a natural-language summary |
| `--ai-provider groq\|gemini\|openai_compatible` | Chooses the AI provider |
| `--fail-on breaking\|warning\|none` | Determines at which level exit code 1 is returned |
| `--quiet` | Only prints the summary line |

### `specshift diff-graphql <old> <new>`

Compares two GraphQL SDL schemas. `<old>` and `<new>` can be a file path,
an http(s) URL, or raw SDL text. Supports the same `--format`, `--output`,
`--ai`, and `--fail-on` options as `diff`.

```bash
specshift diff-graphql old_schema.graphql new_schema.graphql
```

### `specshift check`

Designed for CI/CD. Compares the current specification file against a git
reference (branch, tag, or commit) defined in `.specshift.yml`.

```bash
specshift check --spec openapi.yaml --base-ref origin/main
```

### `specshift watch <url>`

Periodically checks a remote specification, compares it against the
previous snapshot, and sends a notification if a difference is found.

```bash
specshift watch https://api.example.com/openapi.json \
  --interval 600 \
  --slack-webhook "$SLACK_WEBHOOK_URL"
```

### `specshift init`

Creates a sample `.specshift.yml` file.

## Using it with GitHub Actions

### Option 1: the official SpecShift action (recommended)

SpecShift ships its own composite GitHub Action that runs the contract
check and automatically posts (and keeps updated) a Markdown report as a
pull request comment:

```yaml
name: API Contract Check

on:
  pull_request:
    paths:
      - "openapi.yaml"

permissions:
  pull-requests: write

jobs:
  contract-check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - uses: Lethe044/SpecShift@v1
        with:
          spec-path: openapi.yaml
          fail-on: breaking
          # optional: ai-summary: 'true'
```

Available inputs: `spec-path`, `base-ref` (defaults to the PR base branch),
`fail-on` (`breaking` | `warning` | `none`), `ai-summary` (`true`/`false`),
`ai-provider`, `comment-on-pr` (`true`/`false`), `github-token`, and
`specshift-version` to pin a specific release. Outputs: `has-breaking-changes`,
`breaking-count`, `warning-count`, `info-count`, and `report-path`.

### Option 2: calling the CLI directly

If you'd rather not use the action (or want to combine it with other
tooling), the CLI works just as well on its own:

```yaml
name: API Contract Check

on:
  pull_request:
    paths:
      - "openapi.yaml"

jobs:
  contract-check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - uses: actions/setup-python@v5
        with:
          python-version: "3.12"

      - run: pip install specshift

      - run: specshift check --base-ref origin/${{ github.base_ref }}
```

If you want to add AI-powered PR comments, you can generate a Markdown
report with `specshift check --ai --format markdown --output report.md`
and post it as a PR comment using an action like
`peter-evans/create-or-update-comment`.

## Configuration file (`.specshift.yml`)

```yaml
spec_path: openapi.yaml
base_ref: main
fail_on: breaking

# optional
ai_provider: groq
ai_model: llama-3.3-70b-versatile
slack_webhook: https://hooks.slack.com/services/...
discord_webhook: https://discord.com/api/webhooks/...
ignore_paths: []
```

## When SpecShift calls something breaking

The table below summarizes which severity level applies in the most
common scenarios:

| Change | In a request | In a response |
|---|---|---|
| Field removed | Warning | Breaking |
| New required field added | Breaking | Info |
| New optional field added | Info | Info |
| Field no longer required | Info | Breaking |
| Field became required | Breaking | Info |
| Data type changed | Breaking | Breaking |
| Enum value removed | Breaking | Breaking |
| Endpoint or method removed | Breaking | Breaking |

The same table applies to GraphQL: `input` types behave like requests,
`type`/`interface` fields behave like responses.

## Comparison with other tools

| | SpecShift | Raw JSON/YAML diff | oasdiff / openapi-diff style tools |
|---|---|---|---|
| Context-aware classification | Yes | No | Partially |
| GraphQL support | Yes | No | Rarely |
| Natural-language summary | Yes (optional) | No | No |
| Standalone HTML report | Yes | No | Rarely |
| Free to use | Fully free | Free | Usually free |
| CI integration | Built-in (`check` + official Action) | Manual | Varies |
| Live URL monitoring | Built-in (`watch`) | No | Rarely |

## Roadmap

This project is under active development. Some planned areas:

- Support for gRPC/Protobuf contracts
- Path parameter pattern (regex) and discriminator-level rule refinements
- A hosted, shareable web viewer (beyond the offline HTML report)
- Deeper GraphQL support: directive-aware deprecation reasons, custom
  scalar compatibility hints, and federation-aware schema composition

Feel free to open an issue if you have a feature request.

## Contributing

See [CONTRIBUTING.md](CONTRIBUTING.md) for the contribution guide. Bug
reports, feature requests, and pull requests are always welcome.

## License

This project is licensed under the [MIT License](LICENSE).
