Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,5 @@ fixtures/generated/
.ruff_cache/

# Local opencode config
AGENTS.md
.agents/

25 changes: 25 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# API Contract Guardian

## Purpose
CLI tool that monitors OpenAPI schema diffs, detects breaking changes, generates migration guides, and gates CI pipelines on contract violations.

## Build & Test Commands
- Install: `pip install -e .` or `pip install git+https://github.com/Coding-Dev-Tools/api-contract-guardian.git`
- Test: `pytest`
- Lint: `ruff check .`
- Build: `pip wheel . --wheel-dir dist/`

## Architecture
Key directories:
- `src/api_contract_guardian/` — Main package (CLI, diff engine, migration guide generator)
- `tests/` — Test suite
- `.github/workflows/` — CI/CD (4 workflows)

## Conventions
- Language: Python 3.10+
- Test framework: pytest
- CI: GitHub Actions (4 workflows)
- Formatting: ruff (line-length 120)
- Type checking: py.typed included
- Package: setuptools with src layout
- CLI framework: typer
8 changes: 6 additions & 2 deletions src/api_contract_guardian/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@

from __future__ import annotations

import typer
from pathlib import Path
from typing import Any

import typer

# Lazy imports — jwt+cryptography+deepdiff+yaml add ~200ms at module level.
# Deferring heavy deps to command execution cuts cold start from ~440ms to ~180ms.

Expand Down Expand Up @@ -70,7 +71,7 @@ def _load_and_validate(path: str) -> dict:
except SpecLoadError as e:
from rich.console import Console
Console().print(f"[red]Error loading: {e}[/red]")
raise typer.Exit(code=1)
raise typer.Exit(code=1) from e


def _print_result(result: Any) -> None:
Expand Down Expand Up @@ -121,6 +122,7 @@ def diff(
) -> None:
"""Compare two OpenAPI specs and show all detected changes."""
import json

import yaml

from .diff import diff_specs
Expand Down Expand Up @@ -180,6 +182,7 @@ def check(
) -> None:
"""Gate CI pipeline on breaking changes. Returns exit code 1 if gate fails."""
import json

import yaml

from .diff import diff_specs
Expand Down Expand Up @@ -243,6 +246,7 @@ def migrate(
) -> None:
"""Generate a migration guide between two OpenAPI spec versions."""
import json

import yaml

from .diff import diff_specs
Expand Down
2 changes: 1 addition & 1 deletion tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -639,7 +639,7 @@ def test_main_module_version(self):
# carry over to a separate process).
from revenueholdings_license.rate_limiter import RateLimiter
RateLimiter().reset("api-contract-guardian")
from revenueholdings_license import generate_license_key, Tier
from revenueholdings_license import Tier, generate_license_key
env = os.environ.copy()
env["REVENUEHOLDINGS_LICENSE_KEY"] = generate_license_key(Tier.PRO)
# Ensure all environment variables are string keys and string values to prevent Popen TypeError on Windows
Expand Down