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
22 changes: 11 additions & 11 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,22 @@ name: CI / build
on:
pull_request:
push:
branches: [main]
branches: [ main ]
jobs:
python:
name: python (${{ matrix.python-version }})
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.11","3.12"]
matrix: { python-version: ["3.11","3.12"] }
steps:
- uses: actions/checkout@v5
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
with: { python-version: ${{ matrix.python-version }} }
- run: python -m pip install -U pip
- run: pip install poetry
- run: poetry install --no-interaction
- run: poetry run ruff check .
- run: poetry run black --check .
- run: PYTHONPATH=src poetry run pytest -q
- run: poetry run mypy src
- run: pip install pytest mypy ruff black
- run: ruff check .
- run: black --check .
- run: pytest -q
- name: mypy (solo 3.12)
if: matrix.python-version == '3.12'
run: mypy src
90 changes: 55 additions & 35 deletions src/diff_risk_dashboard/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,39 +3,59 @@
import argparse
import json
import sys
from collections.abc import Mapping
from pathlib import Path

from .core import Summary, summarize


def _print_table(summary: Summary) -> None:
bs = summary["by_severity"]
rows = [
("CRITICAL", bs["CRITICAL"]),
("HIGH", bs["HIGH"]),
("MEDIUM", bs["MEDIUM"]),
("LOW", bs["LOW"]),
("INFO", bs["INFO"]),
]
print("\n=== Diff Risk Summary ===")
print(f"Total findings: {summary['total']}")
print("Severity counts:")
w = max(len(r[0]) for r in rows)
for name, cnt in rows:
print(f" {name:<{w}} : {cnt}")
print(f"Worst severity : {summary['worst']}")
print(f"Risk level : {summary['risk_level']}\n")


def main(argv: list[str] | None = None) -> int:
p = argparse.ArgumentParser(description="Diff Risk Dashboard (APV JSON -> summary)")
p.add_argument("apv_json", help="Path to ai-patch-verifier JSON")
args = p.parse_args(argv)
data = json.loads(Path(args.apv_json).read_text(encoding="utf-8"))
sm = summarize(data)
_print_table(sm)
return 2 if sm["risk_level"] == "red" else (1 if sm["risk_level"] == "yellow" else 0)


if __name__ == "__main__":
sys.exit(main())
from typing import Any

from .core import summarize_apv_json
from .report import to_markdown


def _exit_code(risk: str) -> int:
return {"green": 0, "yellow": 1, "red": 2}.get(risk, 0)


def _print_table(summary: Mapping[str, Any]) -> None:
by = summary.get("by_severity", {}) or {}
print("Severity\tCount")
for sev in ["CRITICAL", "HIGH", "MEDIUM", "LOW", "INFO"]:
print(f"{sev}\t{by.get(sev, by.get(sev.lower(), 0))}")
print(f"TOTAL\t{summary.get('total', 0)}")


def main() -> int:
p = argparse.ArgumentParser(
prog="diff_risk_dashboard", description="Diff Risk Dashboard (APV JSON -> summary)"
)
p.add_argument("input", help="Path o texto JSON de ai-patch-verifier")
p.add_argument(
"-f", "--format", choices=["table", "json", "md"], default="table", help="Formato de salida"
)
p.add_argument("-o", "--output", default="-", help="Archivo de salida; '-' = stdout")
p.add_argument(
"--no-exit-by-risk", action="store_true", help="No ajustar el exit code por nivel de riesgo"
)
args = p.parse_args()

summary: Mapping[str, Any] = summarize_apv_json(args.input)
out: str | None = None
if args.format == "json":
out = json.dumps(summary, indent=2)
elif args.format == "md":
out = to_markdown(summary)
else:
_print_table(summary)

if out is not None:
if args.output == "-":
print(out)
else:
Path(args.output).write_text(out, encoding="utf-8")
print(f"Wrote {args.output}", file=sys.stderr)

risk = str(summary.get("risk", summary.get("risk_level", "green")))
return 0 if args.no_exit_by_risk else _exit_code(risk)


if __name__ == "__main__": # pragma: no cover
raise SystemExit(main())
33 changes: 33 additions & 0 deletions src/diff_risk_dashboard/report.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
from __future__ import annotations

from collections.abc import Mapping
from typing import Any

_SEVERITIES = ["CRITICAL", "HIGH", "MEDIUM", "LOW", "INFO"]


def _get_counts(summary: Mapping[str, Any]) -> dict[str, int]:
counts: dict[str, int] = {}
by = summary.get("by_severity", {}) or {}
if isinstance(by, Mapping):
for sev in _SEVERITIES:
counts[sev] = int(by.get(sev, by.get(sev.lower(), 0)) or 0)
return counts


def to_markdown(summary: Mapping[str, Any]) -> str:
counts = _get_counts(summary)
total = int(summary.get("total", 0) or 0)
worst = str(summary.get("worst", "INFO") or "INFO").upper()
risk = str(summary.get("risk", summary.get("risk_level", "green")) or "green").lower()
emoji = {"red": "🔴", "yellow": "🟡", "green": "🟢"}.get(risk, "🟢")
lines = [
f"# Diff Risk Dashboard {emoji} — Worst: **{worst}**",
"",
"| Severity | Count |",
"|---|---:|",
]
for sev in _SEVERITIES:
lines.append(f"| {sev} | {counts.get(sev, 0)} |")
lines += [f"| **TOTAL** | **{total}** |", "", "> Generated by diff-risk-dashboard CLI"]
return "\n".join(lines)
Loading