Metadata-Version: 2.4
Name: pactwatch
Version: 0.1.0
Summary: An OpenAPI breaking-change detector that knows which downstream consumers will actually break.
Author: Mujeeb Lawal-Saka
License-Expression: Apache-2.0
Project-URL: Homepage, https://github.com/pyjeebz/pactwatch
Project-URL: Repository, https://github.com/pyjeebz/pactwatch
Project-URL: Issues, https://github.com/pyjeebz/pactwatch/issues
Keywords: openapi,breaking-changes,microservices,api,ci-cd
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Testing
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: typer>=0.9
Requires-Dist: rich>=13.0
Requires-Dist: prance>=23.6
Requires-Dist: openapi-spec-validator>=0.7
Requires-Dist: pyyaml>=6.0
Provides-Extra: github
Requires-Dist: pygithub>=2.0; extra == "github"
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pytest-cov>=4.0; extra == "dev"
Dynamic: license-file

# PactWatch

> Your microservices have a contract. PactWatch enforces it before merge.

An OpenAPI breaking-change detector that knows which downstream consumers will actually break. Most API diff tools tell you *what* changed. PactWatch tells you *who cares*.

## The Problem

Team A changes their API. Team B's mobile app breaks in production. The spec diff showed "field removed" — but nobody connected it to Team B.

PactWatch maintains a service-to-consumer graph (`pactwatch.yaml`). When a producer's spec changes, it classifies each change as **BREAKING**, **RISKY**, or **SAFE**, then filters the results per consumer. Same spec change, different verdict per team.

## How It Works

```
1. Diff    — structurally compare two OpenAPI 3.x specs
2. Classify — label each change: BREAKING / RISKY / SAFE
3. Filter  — check the consumer graph: who uses the affected endpoints?
```

## Installation

```bash
pip install pactwatch
```

## Quick Start

### Raw diff (Phase 1)

Compare two specs and get classified changes:

```bash
# Rich terminal output
pactwatch diff old-spec.yaml new-spec.yaml

# JSON for CI
pactwatch diff old-spec.yaml new-spec.yaml --format json
```

### Per-consumer impact (Phase 2)

Define your service topology in `pactwatch.yaml`:

```yaml
version: 1
producers:
  api:
    spec: ./services/api/openapi.yaml

consumers:
  mobile-app:
    consumes:
      - producer: api
        endpoints:
          - GET /users/{id}
          - POST /orders
  web-dashboard:
    consumes:
      - producer: api
        endpoints:
          - GET /users/{id}
          - GET /admin/*
```

Then check per-consumer impact:

```bash
# Check all consumers
pactwatch check -c pactwatch.yaml -p api --old old.yaml --new new.yaml

# Check a single consumer
pactwatch check -c pactwatch.yaml -p api --old old.yaml --new new.yaml --consumer mobile-app

# JSON for CI
pactwatch check -c pactwatch.yaml -p api --old old.yaml --new new.yaml -f json
```

### GitHub Action (Phase 3)

Add to your workflow:

```yaml
name: API Contract Check
on: pull_request

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

      - uses: pyjeebz/pactwatch@v1
        with:
          config: pactwatch.yaml
          producer: api
          old-spec: services/api/openapi.yaml  # from base branch
          new-spec: services/api/openapi.yaml  # from PR branch
          github-token: ${{ secrets.GITHUB_TOKEN }}
```

PactWatch will:
- Post a PR comment showing per-consumer impact
- Set a commit status check (pass/fail)
- Exit with code 1 if breaking changes affect any consumer

## Classification Rules

### Breaking
| Change | Why |
|--------|-----|
| Endpoint removed | Consumers calling it will get 404s |
| Required response field removed | Consumers parsing it will crash |
| Response field type changed | Consumers deserializing it will get wrong types |
| New required request field | Existing client code won't send it |
| Status code removed | Consumers handling it will get unexpected responses |
| Auth scheme changed | Existing tokens/keys will stop working |

### Risky
| Change | Why |
|--------|-----|
| Optional field made required | Consumers not sending it may start failing |
| Field made nullable | Consumers not handling null may crash |
| Enum value removed | Consumers matching on it will miss cases |

### Safe
| Change | Why |
|--------|-----|
| New endpoint | Existing consumers unaffected |
| New optional request field | Old clients just don't send it |
| New response field | Old clients ignore extra fields |
| New enum value | Old clients still see their known values |
| Documentation changes | No runtime impact |

## CLI Reference

### `pactwatch diff`

```
pactwatch diff OLD NEW [--format text|json]
```

Compare two OpenAPI specs. Exits with code 1 if BREAKING changes found.

### `pactwatch check`

```
pactwatch check --config PATH --producer NAME --old PATH --new PATH
                [--consumer NAME] [--format text|json]
```

Check per-consumer impact using the consumer graph. Exits with code 1 if any consumer has BREAKING changes.

## Exit Codes

| Code | Meaning |
|------|---------|
| 0 | No breaking changes |
| 1 | Breaking changes detected |
| 2 | Input error (bad spec, bad config, unknown producer) |

## Demo

See the [demo monorepo](examples/demo-monorepo/) for a working example with 3 services.

```bash
# Run the demo
pactwatch check \
  -c examples/demo-monorepo/pactwatch.yaml \
  -p user-api \
  --old examples/demo-monorepo/services/producer-api/openapi.yaml \
  --new examples/demo-monorepo/services/producer-api/openapi-breaking.yaml
```

## License

Apache 2.0
