#!/usr/bin/env python3 """Combine normalized tool reports into one schema-valid project report.""" import argparse import json from pathlib import Path from jsonschema import Draft202012Validator, FormatChecker def main() -> int: parser = argparse.ArgumentParser() parser.add_argument("inputs", nargs="+", type=Path) parser.add_argument("--output", type=Path, required=True) parser.add_argument("--schema", type=Path, default=Path("schemas/test-report.schema.json")) args = parser.parse_args() reports = [json.loads(path.read_text(encoding="utf-8")) for path in args.inputs] if not reports: parser.error("at least one normalized report is required") project = reports[0]["project"] run = reports[0]["run"] results = [] raw_artifacts = [] for report in reports: if report["project"]["id"] != project["id"]: parser.error("all reports must belong to the same project") tool = report["tool"] for result in report["results"]: combined = dict(result) combined["id"] = f"{tool['id']}:{result['id']}" combined["category"] = f"{tool['name']} | {result.get('category', '')}".rstrip(" |") results.append(combined) raw_artifacts.extend(report.get("raw_artifacts", [])) counts = dict.fromkeys(("passed", "failed", "errors", "skipped", "review"), 0) for result in results: counter = "errors" if result["status"] == "error" else result["status"] counts[counter] += 1 scored = counts["passed"] + counts["failed"] aggregate = { "schema_version": "1.0.0", "run": run, "project": project, "tool": {"id": "combined", "name": "Combined test methodologies", "adapter_version": "1.0.0"}, "target": {"id": "project-scope", "type": "test_environment", "groups": []}, "summary": { "total": len(results), **counts, "score": round(counts["passed"] / scored * 100, 2) if scored else 0, }, "results": results, "raw_artifacts": sorted(set(raw_artifacts)), } schema = json.loads(args.schema.read_text(encoding="utf-8")) Draft202012Validator(schema, format_checker=FormatChecker()).validate(aggregate) args.output.parent.mkdir(parents=True, exist_ok=True) args.output.write_text(json.dumps(aggregate, indent=2) + "\n", encoding="utf-8") print(f"Aggregated {len(reports)} reports with {len(results)} results") return 0 if __name__ == "__main__": raise SystemExit(main())