Coverage for src / rhiza_tools / commands / generate_badge.py: 100%

44 statements  

« prev     ^ index     » next       coverage.py v7.13.4, created at 2026-02-23 01:10 +0000

1#!/usr/bin/env python3 

2"""Generate a coverage badge endpoint JSON for shields.io. 

3 

4This script reads a coverage report JSON file and creates a shields.io endpoint 

5JSON file for a coverage badge at a specified output path. 

6""" 

7 

8import json 

9import sys 

10from pathlib import Path 

11 

12from rhiza_tools import console 

13 

14 

15def get_badge_color(coverage: int) -> str: 

16 """Determine badge color based on coverage percentage. 

17 

18 Colors follow a common convention where higher coverage gets "greener" colors. 

19 

20 Args: 

21 coverage: Coverage percentage (0-100). 

22 

23 Returns: 

24 Color name for shields.io badge. 

25 

26 Example: 

27 >>> color = get_badge_color(95) 

28 >>> print(color) 

29 brightgreen 

30 

31 >>> color = get_badge_color(45) 

32 >>> print(color) 

33 red 

34 """ 

35 if coverage >= 90: 

36 return "brightgreen" 

37 elif coverage >= 80: 

38 return "green" 

39 elif coverage >= 70: 

40 return "yellowgreen" 

41 elif coverage >= 60: 

42 return "yellow" 

43 elif coverage >= 50: 

44 return "orange" 

45 else: 

46 return "red" 

47 

48 

49def generate_coverage_badge_command( 

50 coverage_json_path: Path, 

51 output_path: Path, 

52) -> None: 

53 """Generate coverage badge JSON from coverage report. 

54 

55 Reads a pytest-cov generated coverage.json file and creates a shields.io 

56 endpoint JSON file with appropriate color coding based on coverage percentage. 

57 

58 Args: 

59 coverage_json_path: Path to the coverage.json file. 

60 output_path: Path where the badge JSON should be written. 

61 

62 Raises: 

63 SystemExit: If the coverage JSON is invalid or missing required data. 

64 

65 Example: 

66 Generate badge from coverage report:: 

67 

68 from pathlib import Path 

69 generate_coverage_badge_command( 

70 Path("_tests/coverage.json"), 

71 Path("_book/tests/coverage-badge.json") 

72 ) 

73 

74 The generated JSON can be used with shields.io:: 

75 

76 https://img.shields.io/endpoint?url=<url-to-badge-json> 

77 """ 

78 # Check if coverage.json exists 

79 if not coverage_json_path.exists(): 

80 console.warning( 

81 f"Coverage JSON file not found at {coverage_json_path}, skipping badge generation", 

82 ) 

83 return 

84 

85 console.info(f"Generating coverage badge from {coverage_json_path}...") 

86 

87 # Read and parse coverage data 

88 try: 

89 with coverage_json_path.open("r") as f: 

90 data = json.load(f) 

91 except json.JSONDecodeError as e: 

92 console.error(f"Failed to parse coverage JSON: {e}") 

93 sys.exit(1) 

94 

95 # Extract coverage percentage 

96 try: 

97 percent = data["totals"]["percent_covered"] 

98 except KeyError as e: 

99 console.error(f"Missing expected key in coverage JSON: {e}") 

100 sys.exit(1) 

101 

102 # Round to nearest integer 

103 coverage = round(percent) 

104 

105 if not 0 <= coverage <= 100: 

106 console.error(f"Coverage percentage {coverage} is out of valid range 0-100") 

107 sys.exit(1) 

108 

109 console.info(f"Coverage: {coverage}%") 

110 

111 # Determine badge color 

112 color = get_badge_color(coverage) 

113 

114 # Create output directory if it doesn't exist 

115 output_path.parent.mkdir(parents=True, exist_ok=True) 

116 

117 # Generate shields.io endpoint JSON 

118 badge_data = { 

119 "schemaVersion": 1, 

120 "label": "coverage", 

121 "message": f"{coverage}%", 

122 "color": color, 

123 } 

124 

125 with output_path.open("w") as f: 

126 json.dump(badge_data, f, indent=2) 

127 f.write("\n") # Add trailing newline 

128 

129 console.info(f"Coverage badge JSON generated at {output_path}")