File size: 7,703 Bytes
6d82353 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 | #!/usr/bin/env python3
"""
Auto-Guardian: Report Generator
================================
Generate detailed reports about code quality and send appropriate notifications
"""
import json
import sys
from dataclasses import dataclass, field
from datetime import datetime
from enum import Enum
from pathlib import Path
from typing import Optional
class ReportType(Enum):
"""Types of reports"""
PULL_REQUEST = "pull_request"
DAILY_SUMMARY = "daily_summary"
SECURITY_ALERT = "security_alert"
QUALITY_REPORT = "quality_report"
@dataclass
class ReportConfig:
"""Report configuration"""
scan_results_file: str
pr_number: Optional[int] = None
report_type: ReportType = ReportType.PULL_REQUEST
include_details: bool = True
include_suggestions: bool = True
class ReportGenerator:
"""Report generator"""
def __init__(self, config: ReportConfig):
self.config = config
self.results = self._load_results()
def _load_results(self) -> dict:
"""Load scan results"""
with open(self.config.scan_results_file, 'r', encoding='utf-8') as f:
return json.load(f)
def generate_pull_request_comment(self) -> str:
"""Generate comment for Pull Request"""
summary = self.results.get('summary', {})
critical_issues = self.results.get('critical_issues', [])
auto_fixable = self.results.get('auto_fixable_issues', [])
all_issues = self.results.get('all_issues', [])
# Severity emojis
severity_emojis = {
'critical': 'Critical',
'high': 'High',
'medium': 'Medium',
'low': 'Low',
'info': 'Info'
}
report = []
# Title
report.append("## Quality Scan Report - Auto-Guardian")
report.append("")
report.append(f"**Scan Date:** {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
report.append(f"**Files Scanned:** {summary.get('files_scanned', 0)}")
report.append(f"**Total Issues:** {summary.get('total_issues', 0)}")
report.append("")
# Summary by severity
report.append("### Issues Summary")
report.append("")
report.append("| Severity | Count |")
report.append("|----------|-------|")
for severity, count in summary.get('by_severity', {}).items():
emoji = severity_emojis.get(severity, 'Info')
report.append(f"| {emoji} | {count} |")
report.append("")
# Auto-fix status
if auto_fixable:
report.append("### Auto-Fixes Applied")
report.append("")
report.append(f"**{len(auto_fixable)}** issues were fixed automatically:")
report.append("")
for issue in auto_fixable[:10]: # Show first 10 only
file_path = Path(issue['file']).name
report.append(f"- Fixed `{file_path}`:{issue['line']} - {issue['message']}")
if len(auto_fixable) > 10:
report.append(f"- ... and **{len(auto_fixable) - 10}** more fixes")
report.append("")
# Issues requiring human intervention
if critical_issues:
report.append("### Issues Requiring Human Intervention")
report.append("")
report.append("**This code cannot be merged until these issues are resolved:**")
report.append("")
for issue in critical_issues:
file_path = Path(issue['file']).name
emoji = severity_emojis.get(issue['severity'], 'Critical')
report.append(f"- **{emoji} {issue['file']}:{issue['line']}**")
report.append(f" - Issue: {issue['message']}")
if issue.get('suggestion'):
report.append(f" - Suggestion: {issue['suggestion']}")
report.append("")
report.append("---")
report.append("### Merge Status: Blocked")
report.append("")
report.append("**This Pull Request is blocked from merging due to critical issues.**")
report.append("")
report.append("Please resolve the issues above and try again.")
else:
# No critical issues
report.append("---")
report.append("### Merge Status: Approved")
report.append("")
report.append("**This code passed all quality checks!**")
report.append("")
report.append("You can proceed with merging this Pull Request.")
# Footer
report.append("")
report.append("---")
report.append("*Report generated automatically by Auto-Guardian Bot*")
return '\n'.join(report)
def generate_daily_summary(self) -> dict:
"""Generate daily summary"""
summary = self.results.get('summary', {})
return {
"date": datetime.now().isoformat(),
"total_issues": summary.get('total_issues', 0),
"critical_issues": summary.get('critical_count', 0),
"auto_fixed": summary.get('auto_fixable_count', 0),
"by_severity": summary.get('by_severity', {}),
"by_type": summary.get('by_type', {})
}
def generate_security_alert(self) -> str:
"""Generate security alert"""
critical = self.results.get('critical_issues', [])
security_issues = [i for i in critical if 'security' in i.get('type', '')]
if not security_issues:
return None
alert = []
alert.append("Security Alert - Auto-Guardian")
alert.append("")
alert.append("Security vulnerabilities detected in code:")
alert.append("")
for issue in security_issues:
alert.append(f"- {issue['file']}:{issue['line']}")
alert.append(f" {issue['message']}")
if issue.get('suggestion'):
alert.append(f" Suggestion: {issue['suggestion']}")
return '\n'.join(alert)
def save_report(self, content: str, filename: str = "report.md") -> Path:
"""Save report to file"""
output_path = Path(filename)
with open(output_path, 'w', encoding='utf-8') as f:
f.write(content)
return output_path
def main():
"""Main function"""
import argparse
parser = argparse.ArgumentParser(description='Auto-Guardian Report Generator')
parser.add_argument('--scan-results', required=True, help='Scan results file')
parser.add_argument('--pr-number', type=int, help='Pull Request number')
parser.add_argument('--output', '-o', default="report.md", help='Output file')
parser.add_argument('--format', choices=['comment', 'summary', 'json'],
default='comment', help='Report format')
args = parser.parse_args()
config = ReportConfig(
scan_results_file=args.scan_results,
pr_number=args.pr_number,
report_type=ReportType.PULL_REQUEST
)
generator = ReportGenerator(config)
if args.format == 'comment':
report = generator.generate_pull_request_comment()
elif args.format == 'summary':
report = json.dumps(generator.generate_daily_summary(), indent=2)
else:
report = generator.generate_pull_request_comment()
# Print report
print(report)
# Save report
generator.save_report(report, args.output)
print(f"\nReport saved to {args.output}")
if __name__ == '__main__':
main()
|