ci(ariadne): enforce per-file coverage contract
This commit is contained in:
parent
bdb9b47291
commit
e36fc5229d
6
Jenkinsfile
vendored
6
Jenkinsfile
vendored
@ -196,11 +196,11 @@ if [ "${install_rc}" -eq 0 ]; then
|
|||||||
-m pytest -ra -vv --durations=20 --junitxml "${JUNIT_XML}"
|
-m pytest -ra -vv --durations=20 --junitxml "${JUNIT_XML}"
|
||||||
tests_rc=$?
|
tests_rc=$?
|
||||||
python -c "import json; payload=json.load(open('build/coverage.json', encoding='utf-8')); percent=(payload.get('summary') or {}).get('percent_covered'); print(f'Coverage summary: {percent:.2f}%' if percent is not None else 'Coverage summary unavailable')" || true
|
python -c "import json; payload=json.load(open('build/coverage.json', encoding='utf-8')); percent=(payload.get('summary') or {}).get('percent_covered'); print(f'Coverage summary: {percent:.2f}%' if percent is not None else 'Coverage summary unavailable')" || true
|
||||||
if [ -f "${COVERAGE_JSON}" ] && [ -f scripts/check_coverage_contract.py ] && [ -f ci/coverage_contract.json ]; then
|
if [ -f "${COVERAGE_JSON}" ] && [ -f scripts/check_coverage_contract.py ]; then
|
||||||
python scripts/check_coverage_contract.py "${COVERAGE_JSON}" ci/coverage_contract.json
|
python scripts/check_coverage_contract.py "${COVERAGE_JSON}" --source-root ariadne --threshold "${COVERAGE_MIN}"
|
||||||
coverage_contract_rc=$?
|
coverage_contract_rc=$?
|
||||||
else
|
else
|
||||||
echo "coverage contract check skipped: checker, contract, or coverage report missing"
|
echo "coverage contract check skipped: checker or coverage report missing"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
printf '%s\n' "${docs_rc}" > build/docs-naming.rc
|
printf '%s\n' "${docs_rc}" > build/docs-naming.rc
|
||||||
|
|||||||
66
scripts/check_coverage_contract.py
Normal file
66
scripts/check_coverage_contract.py
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Enforce Ariadne's per-file source coverage contract."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
def _source_files(root: Path) -> list[str]:
|
||||||
|
files: list[str] = []
|
||||||
|
for path in sorted(root.rglob("*.py")):
|
||||||
|
if "__pycache__" in path.parts:
|
||||||
|
continue
|
||||||
|
files.append(path.as_posix())
|
||||||
|
return files
|
||||||
|
|
||||||
|
|
||||||
|
def _coverage_percent(file_payload: object) -> float | None:
|
||||||
|
if not isinstance(file_payload, dict):
|
||||||
|
return None
|
||||||
|
summary = file_payload.get("summary")
|
||||||
|
if not isinstance(summary, dict):
|
||||||
|
return None
|
||||||
|
value = summary.get("percent_covered")
|
||||||
|
if isinstance(value, (int, float)):
|
||||||
|
return float(value)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
parser = argparse.ArgumentParser(description=__doc__)
|
||||||
|
parser.add_argument("coverage_json")
|
||||||
|
parser.add_argument("--source-root", default="ariadne")
|
||||||
|
parser.add_argument("--threshold", type=float, default=95.0)
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
coverage_path = Path(args.coverage_json)
|
||||||
|
source_root = Path(args.source_root)
|
||||||
|
payload = json.loads(coverage_path.read_text(encoding="utf-8"))
|
||||||
|
files = payload.get("files") if isinstance(payload, dict) else None
|
||||||
|
if not isinstance(files, dict):
|
||||||
|
print(f"{coverage_path}: missing files coverage map")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
failures: list[str] = []
|
||||||
|
for source_file in _source_files(source_root):
|
||||||
|
percent = _coverage_percent(files.get(source_file))
|
||||||
|
if percent is None:
|
||||||
|
failures.append(f"{source_file}: missing from coverage report")
|
||||||
|
elif percent < args.threshold:
|
||||||
|
failures.append(f"{source_file}: {percent:.2f}% below {args.threshold:.2f}%")
|
||||||
|
|
||||||
|
if failures:
|
||||||
|
print("coverage contract failed:")
|
||||||
|
for failure in failures:
|
||||||
|
print(f" - {failure}")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
print(f"coverage contract passed: {len(_source_files(source_root))} files >= {args.threshold:.2f}%")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
raise SystemExit(main())
|
||||||
Loading…
x
Reference in New Issue
Block a user