Skip to content
Merged
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
9 changes: 5 additions & 4 deletions .github/scripts/pr_summary.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Build one concise PR summary from .apiverity-artifacts/*.json."""

from __future__ import annotations

import glob
Expand All @@ -16,13 +17,13 @@
removed = sum(1 for f in findings if f["rule_id"] == "BRK-OP-REMOVED")
added = sum(1 for f in findings if f["rule_id"] == "BRK-OP-ADDED")
semver = [f for f in findings if f["rule_id"].startswith("SEMVER-")]
verdict = ("✅ respected" if not semver
else "❌ " + "; ".join(f["rule_id"] for f in semver))
verdict = "✅ respected" if not semver else "❌ " + "; ".join(f["rule_id"] for f in semver)
lines.append(f"### `{pathlib.Path(path).stem.replace('_', '/')}`")
lines.append(f"- Breaking: **{len(breaking)}** · Warnings: **{len(warnings)}**")
lines.append(f"- Removed endpoints: {removed} · New endpoints: {added}")
lines.append(f"- Semver ({data.get('old_version', '?')} → "
f"{data.get('new_version', '?')}): {verdict}")
lines.append(
f"- Semver ({data.get('old_version', '?')} → {data.get('new_version', '?')}): {verdict}"
)
for f in (breaking + warnings)[:10]:
lines.append(f" - `{f['rule_id']}` ({f['severity']}) {f['message']}")

Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ jobs:
python -m pip install --upgrade pip
pip install -e ".[dev]"
- name: Ruff lint
run: ruff check apiverity tests scripts
run: ruff check apiverity tests scripts .github/scripts
- name: Ruff format check
run: ruff format --check apiverity tests scripts
run: ruff format --check apiverity tests scripts .github/scripts
- name: Type check
run: mypy apiverity
- name: Tests with coverage
Expand Down
29 changes: 28 additions & 1 deletion apiverity/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,32 @@
"""api-verity-lab: unified API contract governance and reliability testing."""

__version__ = "0.1.0"
from __future__ import annotations


def _installed_version() -> str:
"""The version actually installed, not a literal in this file.

This was hardcoded to "0.1.0" while ``pyproject.toml`` said 0.2.0, so
``apiverity --version`` (which the bug-report template asks reporters to
run) would have reported a release that does not exist. Deriving it from
the distribution's own metadata makes that drift unrepresentable.
"""
from importlib.metadata import PackageNotFoundError, version

try:
return version("api-verity-lab")
except PackageNotFoundError: # source tree, not installed
import pathlib

pyproject = pathlib.Path(__file__).resolve().parent.parent / "pyproject.toml"
try:
import tomllib

return str(tomllib.loads(pyproject.read_text(encoding="utf-8"))["project"]["version"])
except Exception:
return "unknown"


__version__ = _installed_version()

PLUGIN_API_VERSION = "1"
2 changes: 2 additions & 0 deletions apiverity/cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import sys
from typing import Any

from apiverity import __version__
from apiverity.cli.commands.artifacts import cmd_export, cmd_report, cmd_serve
from apiverity.cli.commands.common import EXIT_INTERNAL, EXIT_OK
from apiverity.cli.commands.governance import (
Expand Down Expand Up @@ -68,6 +69,7 @@

def build_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(prog="apiverity", description=__doc__)
parser.add_argument("--version", action="version", version=f"apiverity {__version__}")
sub = parser.add_subparsers(dest="command", required=True)

p = sub.add_parser("validate")
Expand Down
50 changes: 50 additions & 0 deletions tests/unit/test_version_is_reportable.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
"""The version has to be reportable, and it has to be the real one.

`apiverity --version` did not exist: argparse rejected the flag and exited 2 —
while `.github/ISSUE_TEMPLATE/bug_report.md` asks every reporter to run exactly
that. Separately, `__version__` was pinned to "0.1.0" in the package while
`pyproject.toml` said 0.2.0, so even once the flag worked it would have named a
release that was never cut.

These two tests are what makes both unrepresentable going forward.
"""

from __future__ import annotations

import pathlib
import tomllib

import pytest

from apiverity import __version__
from apiverity.cli.main import main

PYPROJECT = pathlib.Path(__file__).resolve().parents[2] / "pyproject.toml"


def _declared_version() -> str:
data = tomllib.loads(PYPROJECT.read_text(encoding="utf-8"))
return str(data["project"]["version"])


def test_package_version_matches_pyproject() -> None:
assert __version__ == _declared_version()


def test_version_flag_exits_zero_and_prints_the_version(
capsys: pytest.CaptureFixture[str],
) -> None:
with pytest.raises(SystemExit) as excinfo:
main(["--version"])
assert excinfo.value.code == 0
assert __version__ in capsys.readouterr().out


def test_bug_report_template_asks_for_a_command_that_works() -> None:
"""The template is the reason this flag has to exist."""
template = PYPROJECT.parent / ".github/ISSUE_TEMPLATE/bug_report.md"
if "apiverity --version" not in template.read_text(encoding="utf-8"):
pytest.skip("template no longer asks for --version")
with pytest.raises(SystemExit) as excinfo:
main(["--version"])
assert excinfo.value.code == 0
Loading