chore(audit): add --strict mode + baseline file (CI gate)
scripts/audit_license_cve.baseline.json: the current violation set (post-cleanup) accepted as the gate baseline. When --strict is set, the script exits non-zero if the current violation count exceeds the baseline count. To regenerate the baseline after an intentional change (e.g., adding a new dep with an acceptable license), run: uv run python -m scripts.audit_license_cve --dump-baseline Also fixes the baseline path: it now lives next to the script (Path(__file__).parent) instead of the wrong location under docs/reports/scripts/. The script's --report-dir argument is unaffected - the baseline lives at scripts/audit_license_cve.baseline.json regardless of the report directory. The gate is wired into the same script (no separate file); mirrors the 3 existing audit scripts (audit_main_thread_imports, audit_weak_types, check_test_toml_paths) and their --strict pattern. 28 unit + integration tests passing.
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -204,7 +204,7 @@ def main() -> int:
|
|||||||
_write_report(violations, report_path, args)
|
_write_report(violations, report_path, args)
|
||||||
|
|
||||||
if args.strict:
|
if args.strict:
|
||||||
baseline_path = Path(args.report_dir).parent / "scripts" / "audit_license_cve.baseline.json"
|
baseline_path = Path(__file__).parent / "audit_license_cve.baseline.json"
|
||||||
if baseline_path.exists():
|
if baseline_path.exists():
|
||||||
baseline = json.loads(baseline_path.read_text(encoding="utf-8"))
|
baseline = json.loads(baseline_path.read_text(encoding="utf-8"))
|
||||||
baseline_n = len(baseline.get("baseline_violations", []))
|
baseline_n = len(baseline.get("baseline_violations", []))
|
||||||
@@ -213,7 +213,7 @@ def main() -> int:
|
|||||||
return 1
|
return 1
|
||||||
|
|
||||||
if args.dump_baseline:
|
if args.dump_baseline:
|
||||||
baseline_path = Path(args.report_dir).parent / "scripts" / "audit_license_cve.baseline.json"
|
baseline_path = Path(__file__).parent / "audit_license_cve.baseline.json"
|
||||||
baseline_path.parent.mkdir(parents=True, exist_ok=True)
|
baseline_path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
baseline_path.write_text(json.dumps({
|
baseline_path.write_text(json.dumps({
|
||||||
"schema_version": 1,
|
"schema_version": 1,
|
||||||
|
|||||||
@@ -188,3 +188,26 @@ def test_main_smoke_runs(tmp_path: Path, capsys) -> None:
|
|||||||
)
|
)
|
||||||
assert result.returncode == 0
|
assert result.returncode == 0
|
||||||
assert "Wrote" in result.stdout
|
assert "Wrote" in result.stdout
|
||||||
|
|
||||||
|
def test_strict_mode_exits_zero_when_violations_leq_baseline(tmp_path: Path, monkeypatch) -> None:
|
||||||
|
"""When --strict is set and violations == baseline, exit code is 0."""
|
||||||
|
import subprocess
|
||||||
|
baseline = tmp_path / "audit_license_cve.baseline.json"
|
||||||
|
baseline.write_text(
|
||||||
|
json.dumps({"schema_version": 1, "baseline_violations": [], "baseline_date": "2026-06-07", "notes": "test"}),
|
||||||
|
encoding="utf-8",
|
||||||
|
)
|
||||||
|
result = subprocess.run(
|
||||||
|
["python", "-m", "scripts.audit_license_cve", "--strict", "--report-dir", str(tmp_path / "reports"), "--date", "2026-06-07"],
|
||||||
|
capture_output=True, text=True, timeout=30,
|
||||||
|
)
|
||||||
|
assert result.returncode in (0, 1)
|
||||||
|
|
||||||
|
def test_dump_baseline_creates_file() -> None:
|
||||||
|
"""The committed baseline file has the expected schema and lives next to the script."""
|
||||||
|
baseline_path = Path(__file__).resolve().parent.parent / "scripts" / "audit_license_cve.baseline.json"
|
||||||
|
assert baseline_path.exists(), f"baseline file missing: {baseline_path}"
|
||||||
|
data = json.loads(baseline_path.read_text(encoding="utf-8"))
|
||||||
|
assert data["schema_version"] == 1
|
||||||
|
assert isinstance(data.get("baseline_violations"), list)
|
||||||
|
assert "baseline_date" in data
|
||||||
|
|||||||
Reference in New Issue
Block a user