Coverage for src/pytribeam/cicd/report_badges.py: 52%
56 statements
« prev ^ index » next coverage.py v7.5.1, created at 2026-07-23 14:51 +0000
« prev ^ index » next coverage.py v7.5.1, created at 2026-07-23 14:51 +0000
1#!/usr/bin/env python3
2"""
3Generates SVG badges for Lint and Coverage scores.
4"""
6import argparse
7import os
8import re
9import xml.etree.ElementTree as ET
11from pytribeam.cicd.utilities import (
12 get_score_color_coverage,
13 get_score_color_lint,
14)
17def get_pylint_score(input_file: str) -> str:
18 """Extract score from pylint report text."""
19 if not os.path.exists(input_file): 19 ↛ 20line 19 didn't jump to line 20, because the condition on line 19 was never true
20 return "0.00"
21 with open(input_file, "r", encoding="utf-8") as f:
22 content = f.read()
23 match = re.search(r"Your code has been rated at (\d+\.\d+)/10", content)
24 return match.group(1) if match else "0.00"
27def get_coverage_score(input_file: str) -> str:
28 """Extract percentage from coverage XML."""
29 if not os.path.exists(input_file): 29 ↛ 30line 29 didn't jump to line 30, because the condition on line 29 was never true
30 return "0.0"
31 try:
32 tree = ET.parse(input_file)
33 root = tree.getroot()
34 lines_valid = float(root.attrib.get("lines-valid", 0))
35 lines_covered = float(root.attrib.get("lines-covered", 0))
36 branches_valid = float(root.attrib.get("branches-valid", 0))
37 branches_covered = float(root.attrib.get("branches-covered", 0))
38 if lines_valid == 0: 38 ↛ 39line 38 didn't jump to line 39, because the condition on line 38 was never true
39 return "0.0"
40 return f"{((lines_covered + branches_covered) / (lines_valid + branches_valid) * 100):.1f}"
41 except Exception:
42 return "0.0"
45def generate_badge_svg(label: str, value: str, color_key: str) -> str:
46 """Generate a flat-style SVG badge."""
47 # Map color keys to hex
48 color_map = {
49 "brightgreen": "#4c1",
50 "green": "#97ca00",
51 "yellow": "#dfb317",
52 "orange": "#fe7d37",
53 "red": "#e05d44",
54 "gray": "#9f9f9f",
55 }
56 color = color_map.get(color_key, color_map["gray"])
58 # Simple width calculation
59 label_w = len(label) * 7 + 10
60 value_w = len(value) * 7 + 10
61 total_w = label_w + value_w
63 return f"""<svg xmlns="http://www.w3.org/2000/svg" width="{total_w}" height="20" viewBox="0 0 {total_w} 20" preserveAspectRatio="xMidYMid meet">
64 <linearGradient id="b" x2="0" y2="100%">
65 <stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
66 <stop offset="1" stop-opacity=".1"/>
67 </linearGradient>
68 <mask id="a">
69 <rect width="{total_w}" height="20" rx="3" fill="#fff"/>
70 </mask>
71 <g mask="url(#a)">
72 <path fill="#555" d="M0 0h{label_w}v20H0z"/>
73 <path fill="{color}" d="M{label_w} 0h{value_w}v20H{label_w}z"/>
74 <path fill="url(#b)" d="M0 0h{total_w}v20H0z"/>
75 </g>
76 <g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" font-size="11">
77 <text x="{label_w / 2}" y="15" fill="#010101" fill-opacity=".3">{label}</text>
78 <text x="{label_w / 2}" y="14">{label}</text>
79 <text x="{label_w + value_w / 2}" y="15" fill="#010101" fill-opacity=".3">{value}</text>
80 <text x="{label_w + value_w / 2}" y="14">{value}</text>
81 </g>
82 </svg>"""
85def main():
86 parser = argparse.ArgumentParser(
87 description="Generate SVG badges for CI/CD results."
88 )
89 parser.add_argument("--lint_file", help="Path to pylint report text file")
90 parser.add_argument("--coverage_file", help="Path to coverage XML file")
91 parser.add_argument("--output_dir", required=True, help="Directory to save badges")
93 args = parser.parse_args()
94 os.makedirs(args.output_dir, exist_ok=True)
96 if args.lint_file:
97 score = get_pylint_score(args.lint_file)
98 color = get_score_color_lint(score)
99 svg = generate_badge_svg("lint", f"{score}/10", color)
100 with open(os.path.join(args.output_dir, "lint.svg"), "w") as f:
101 f.write(svg)
102 print(f"[OK] Lint badge generated: {score}/10")
104 if args.coverage_file:
105 score = get_coverage_score(args.coverage_file)
106 color = get_score_color_coverage(score)
107 svg = generate_badge_svg("coverage", f"{score}%", color)
108 with open(os.path.join(args.output_dir, "coverage.svg"), "w") as f:
109 f.write(svg)
110 print(f"[OK] Coverage badge generated: {score}%")
113if __name__ == "__main__":
114 main()