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

1#!/usr/bin/env python3 

2""" 

3Generates SVG badges for Lint and Coverage scores. 

4""" 

5 

6import argparse 

7import os 

8import re 

9import xml.etree.ElementTree as ET 

10 

11from pytribeam.cicd.utilities import ( 

12 get_score_color_coverage, 

13 get_score_color_lint, 

14) 

15 

16 

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" 

25 

26 

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" 

43 

44 

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"]) 

57 

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 

62 

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>""" 

83 

84 

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") 

92 

93 args = parser.parse_args() 

94 os.makedirs(args.output_dir, exist_ok=True) 

95 

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") 

103 

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}%") 

111 

112 

113if __name__ == "__main__": 

114 main()