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
2 changes: 1 addition & 1 deletion .github/workflows/cowork-auto-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
# without this step every run failed with "not a git repository" and no
# PR was ever opened (fleet-wide defect: 11/11 seeded copies lacked it).
- name: Check out the pushed branch
uses: actions/checkout@v7
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
ref: ${{ github.ref_name }}
fetch-depth: 0
Expand Down
64 changes: 16 additions & 48 deletions src/api_contract_guardian/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,7 @@
except ImportError:
import warnings

warnings.warn(
"revenueholdings-license not installed; license checks skipped", stacklevel=2
)
warnings.warn("revenueholdings-license not installed; license checks skipped", stacklevel=2)
_has_rh = False

def require_license(product: str) -> None: # type: ignore[misc]
Expand Down Expand Up @@ -53,15 +51,11 @@ def _require_license(tool_name: str) -> None:
raise


def _validate_output_format(
format_name: str, allowed: tuple[str, ...], command: str
) -> str:
def _validate_output_format(format_name: str, allowed: tuple[str, ...], command: str) -> str:
"""Reject unsupported output formats before the command runs."""
if format_name not in allowed:
allowed_list = ", ".join(allowed)
raise typer.BadParameter(
f"Unsupported {command} format '{format_name}'. Choose from: {allowed_list}"
)
raise typer.BadParameter(f"Unsupported {command} format '{format_name}'. Choose from: {allowed_list}")
return format_name


Expand Down Expand Up @@ -97,9 +91,7 @@ def _app_callback(
) -> None:
"""Detect breaking changes in OpenAPI specs and gate CI pipelines."""
global _require_license_strict
_require_license_strict = require_license_flag or bool(
os.environ.get("REVENUEHOLDINGS_REQUIRE_LICENSE")
)
_require_license_strict = require_license_flag or bool(os.environ.get("REVENUEHOLDINGS_REQUIRE_LICENSE"))


def _load_and_validate(path: str) -> dict:
Expand Down Expand Up @@ -141,17 +133,11 @@ def _print_result(result: Any) -> None:
table.add_column("Severity")

for change in breaking:
table.add_row(
change.path, change.kind, change.description, "[red]Breaking[/red]"
)
table.add_row(change.path, change.kind, change.description, "[red]Breaking[/red]")
for change in dangerous:
table.add_row(
change.path, change.kind, change.description, "[yellow]Dangerous[/yellow]"
)
table.add_row(change.path, change.kind, change.description, "[yellow]Dangerous[/yellow]")
for change in non_breaking:
table.add_row(
change.path, change.kind, change.description, "[green]Non-breaking[/green]"
)
table.add_row(change.path, change.kind, change.description, "[green]Non-breaking[/green]")
for change in info:
table.add_row(change.path, change.kind, change.description, "[blue]Info[/blue]")

Expand All @@ -169,9 +155,7 @@ def diff(
old: str = typer.Argument(..., help="Path to old (baseline) OpenAPI spec"),
new: str = typer.Argument(..., help="Path to new (proposed) OpenAPI spec"),
output: str | None = typer.Option(None, "--output", "-o", help="Output file path"),
format: str = typer.Option(
"rich", "--format", "-f", help="Output format: rich, json, yaml, markdown"
),
format: str = typer.Option("rich", "--format", "-f", help="Output format: rich, json, yaml, markdown"),
) -> None:
"""Compare two OpenAPI specs and show all detected changes."""
import json
Expand All @@ -182,9 +166,7 @@ def diff(
from .migration import generate_migration_guide

_require_license("api-contract-guardian")
format = _validate_output_format(
format, ("rich", "json", "yaml", "markdown"), "diff"
)
format = _validate_output_format(format, ("rich", "json", "yaml", "markdown"), "diff")
old_spec = _load_and_validate(old)
new_spec = _load_and_validate(new)

Expand All @@ -199,9 +181,7 @@ def diff(
else:
console.print(output_data)
elif format == "yaml":
output_data = yaml.safe_dump(
result.to_dict(), sort_keys=False, default_flow_style=False
)
output_data = yaml.safe_dump(result.to_dict(), sort_keys=False, default_flow_style=False)
if output:
Path(output).write_text(output_data, encoding="utf-8")
console.print(f"Written to {output}")
Expand Down Expand Up @@ -236,16 +216,10 @@ def check(
"--fail-on-dangerous/--allow-dangerous",
help="Fail on dangerous changes",
),
max_breaking: int = typer.Option(
-1, "--max-breaking", help="Max breaking changes (-1=defer, 0=none)"
),
max_dangerous: int = typer.Option(
-1, "--max-dangerous", help="Max dangerous changes (-1=defer, 0=none)"
),
max_breaking: int = typer.Option(-1, "--max-breaking", help="Max breaking changes (-1=defer, 0=none)"),
max_dangerous: int = typer.Option(-1, "--max-dangerous", help="Max dangerous changes (-1=defer, 0=none)"),
output: str | None = typer.Option(None, "--output", "-o", help="Output file path"),
format: str = typer.Option(
"rich", "--format", "-f", help="Output format: rich, json, yaml"
),
format: str = typer.Option("rich", "--format", "-f", help="Output format: rich, json, yaml"),
) -> None:
"""Gate CI pipeline on breaking changes. Returns exit code 1 if gate fails."""
import json
Expand Down Expand Up @@ -284,9 +258,7 @@ def check(
"diff": result.to_dict(),
}
if format == "yaml":
output_data = yaml.safe_dump(
payload, sort_keys=False, default_flow_style=False
)
output_data = yaml.safe_dump(payload, sort_keys=False, default_flow_style=False)
else:
output_data = json.dumps(payload, indent=2)
console.print(output_data)
Expand All @@ -297,9 +269,7 @@ def check(
"diff": result.to_dict(),
}
if format == "yaml":
output_data = yaml.safe_dump(
payload, sort_keys=False, default_flow_style=False
)
output_data = yaml.safe_dump(payload, sort_keys=False, default_flow_style=False)
else:
output_data = json.dumps(payload, indent=2)
Path(output).write_text(output_data, encoding="utf-8")
Expand All @@ -313,9 +283,7 @@ def migrate(
old: str = typer.Argument(..., help="Path to old (baseline) OpenAPI spec"),
new: str = typer.Argument(..., help="Path to new (proposed) OpenAPI spec"),
output: str | None = typer.Option(None, "--output", "-o", help="Output file path"),
format: str = typer.Option(
"markdown", "--format", "-f", help="Output format: markdown, json, yaml"
),
format: str = typer.Option("markdown", "--format", "-f", help="Output format: markdown, json, yaml"),
) -> None:
"""Generate a migration guide between two OpenAPI spec versions."""
import json
Expand Down
Loading
Loading