src/pqc_lint/reporters/github.py
1.5 KB · 52 lines · python Raw
1 """GitHub Actions workflow-command reporter.
2
3 Emits `::error file=...,line=...::msg` and `::warning ...::msg` lines that
4 GitHub Actions auto-renders as annotations on PR diffs.
5 """
6
7 from __future__ import annotations
8
9 from io import StringIO
10
11 from pqc_lint.findings import ScanReport, Severity
12 from pqc_lint.reporters.base import Reporter
13
14 _SEVERITY_TO_COMMAND = {
15 Severity.CRITICAL: "error",
16 Severity.HIGH: "error",
17 Severity.MEDIUM: "warning",
18 Severity.LOW: "notice",
19 Severity.INFO: "notice",
20 }
21
22
23 def _escape(value: str) -> str:
24 return (
25 value.replace("%", "%25")
26 .replace("\r", "%0D")
27 .replace("\n", "%0A")
28 )
29
30
31 class GitHubReporter(Reporter):
32 format_name = "github"
33
34 def render(self, report: ScanReport) -> str:
35 buf = StringIO()
36 for f in report.findings:
37 cmd = _SEVERITY_TO_COMMAND[f.severity]
38 title = _escape(f"{f.rule_id}: {f.severity.value.upper()}")
39 message = _escape(f"{f.message} Suggestion: {f.suggestion}")
40 buf.write(
41 f"::{cmd} file={f.file},line={f.line},col={f.column},title={title}::{message}\n"
42 )
43 counts = report.counts_by_severity()
44 buf.write(
45 f"::notice title=pqc-lint summary::"
46 f"Scanned {report.files_scanned} files. "
47 f"Found {len(report.findings)} issues "
48 f"(critical={counts['critical']}, high={counts['high']}, "
49 f"medium={counts['medium']}, low={counts['low']}).\n"
50 )
51 return buf.getvalue()
52