src/pqc_lint/action_runner.py
2.9 KB · 81 lines · python Raw
1 """GitHub Action runner - invoked by action.yml composite step.
2
3 Reads configuration from PQC_LINT_* env vars (set by action.yml inputs)
4 and GITHUB_OUTPUT for workflow outputs.
5 """
6
7 from __future__ import annotations
8
9 import os
10 import sys
11
12 from pqc_lint.findings import Severity
13 from pqc_lint.reporters import REPORTERS
14 from pqc_lint.scanner import DEFAULT_EXCLUDES, Scanner
15
16
17 def _set_output(name: str, value: str) -> None:
18 out_path = os.environ.get("GITHUB_OUTPUT")
19 if not out_path:
20 return
21 with open(out_path, "a", encoding="utf-8") as f:
22 f.write(f"{name}={value}\n")
23
24
25 def run() -> int:
26 path = os.environ.get("PQC_LINT_PATH", ".")
27 fail_on = os.environ.get("PQC_LINT_FAIL_ON", "high")
28 output_format = os.environ.get("PQC_LINT_FORMAT", "github")
29 output_file = os.environ.get("PQC_LINT_OUTPUT", "").strip()
30 excludes_str = os.environ.get("PQC_LINT_EXCLUDE", "")
31 languages_str = os.environ.get("PQC_LINT_LANGUAGES", "")
32
33 lang_tuple = tuple(s.strip().lower() for s in languages_str.split(",") if s.strip())
34 extra_excludes = tuple(s.strip() for s in excludes_str.split(",") if s.strip())
35 excludes = DEFAULT_EXCLUDES + extra_excludes
36
37 scanner = Scanner(languages=lang_tuple, excludes=excludes)
38 report = scanner.scan_path(path)
39
40 # Always emit GitHub annotations for inline PR feedback
41 gh_output = REPORTERS["github"]().render(report)
42 print(gh_output, end="")
43
44 # Optional user-selected format to file or stdout
45 if output_format != "github" or output_file:
46 reporter_out = REPORTERS[output_format]().render(report)
47 if output_file:
48 with open(output_file, "w", encoding="utf-8") as f:
49 f.write(reporter_out)
50
51 # SARIF is always produced if format == sarif OR if the user requested upload
52 sarif_path = ""
53 if output_format == "sarif":
54 sarif_path = output_file or "pqc-lint.sarif"
55 if not output_file:
56 with open(sarif_path, "w", encoding="utf-8") as f:
57 f.write(REPORTERS["sarif"]().render(report))
58 else:
59 # Always produce a default SARIF file so upload-sarif step can use it
60 sarif_path = "pqc-lint.sarif"
61 with open(sarif_path, "w", encoding="utf-8") as f:
62 f.write(REPORTERS["sarif"]().render(report))
63
64 counts = report.counts_by_severity()
65 _set_output("total-findings", str(len(report.findings)))
66 _set_output("critical", str(counts["critical"]))
67 _set_output("high", str(counts["high"]))
68 _set_output("medium", str(counts["medium"]))
69 _set_output("low", str(counts["low"]))
70 _set_output("sarif-path", sarif_path)
71
72 threshold = Severity.from_str(fail_on)
73 if report.has_failing(threshold):
74 print(f"::error::pqc-lint: findings at or above '{fail_on}' severity detected. Failing the build.")
75 return 1
76 return 0
77
78
79 if __name__ == "__main__":
80 sys.exit(run())
81