ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
check_go.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2# SPDX-License-Identifier: MIT
3# Copyright (c) 2026 Brighton Sikarskie
4"""Gate: first-party Go verification (vet, static analysis, tests, coverage).
5
6Scope is derived, not hardcoded
7-------------------------------
8``git ls-files`` enumerates every tracked or untracked-but-not-ignored,
9present ``*.go`` file, so a new module is covered the day it is added with no
10allowlist to forget. Build-output trees (``build/``, ``_deps/``) are dropped
11through :mod:`lint_targets`, matching every other provider.
12
13Verification Modes
14------------------
15- **Lint / Static Analysis** (default): ``go vet ./...`` checks correctness,
16 and ``staticcheck ./...`` runs if available on PATH. Formatting (``gofmt``)
17 is enforced by the format gate (``format_tree.sh``), never here.
18- **Test execution** (``--test``): runs ``go test -race -v ./...`` across every
19 discovered module root.
20- **Coverage gating** (``--coverage``): runs ``go test -cover`` (with ``-coverpkg=./...``)
21 and fails if statement coverage falls below ``--floor <pct>`` (defaulting to 85.0%).
22
23Non-vacuity
24-----------
25``--selftest`` feeds the tools deliberately non-conforming fixtures and asserts they
26fire, clean fixtures and asserts silence, tests the race detector, exercises coverage
27floor enforcement, and verifies the worktree-scope exclusion check. A collapsed scope
28trips the file floor instead of reporting a clean tree.
29
30Run::
31
32 check_go.py # lint gate (vet, staticcheck)
33 check_go.py --test # run tests with race detection
34 check_go.py --coverage # run coverage against floor (default 85%)
35 check_go.py --coverage --floor 90 # run coverage against 90% floor
36 check_go.py --test --coverage # test + coverage
37 check_go.py --require # fail (not skip) if go is absent
38 check_go.py --selftest # prove verification tools fire and stay quiet
39
40Exit 0 if clean, exit 1 on findings or test/coverage failures, exit 2 on a tool
41error or a scope that collapsed below the file floor.
42"""
43
44from __future__ import annotations
45
46import os
47import re
48import shutil
49import subprocess
50import sys
51import tempfile
52from pathlib import Path
53
54sys.path.insert(0, str(Path(__file__).resolve().parent))
55
56from lint_targets import is_build_output_path
57
58
59def _repo_root() -> Path:
60 return Path(__file__).resolve().parents[2]
61
62
63def _find_go() -> str | None:
64 env = os.environ.get("GO")
65 if env and Path(env).exists():
66 return env
67 return shutil.which("go")
68
69
70def _find_staticcheck() -> str | None:
71 """Path to staticcheck on PATH or in STATICCHECK env var, if present."""
72 env = os.environ.get("STATICCHECK")
73 if env and Path(env).exists():
74 return env
75 return shutil.which("staticcheck")
76
77
78def _git_ls_files(*pathspec: str) -> list[str]:
79 """Return tracked or untracked/non-ignored paths matching `pathspec`."""
80 proc = subprocess.run( # noqa: S603 -- fixed argv, trusted tool path
81 [ # noqa: S607 -- trusted: fixed git argv
82 "git",
83 "ls-files",
84 "-z",
85 "--cached",
86 "--others",
87 "--exclude-standard",
88 "--",
89 *pathspec,
90 ],
91 cwd=_repo_root(),
92 capture_output=True,
93 text=True,
94 check=False,
95 )
96 if proc.returncode != 0:
97 sys.stderr.write(proc.stderr)
98 sys.stderr.write("check_go.py: FATAL -- `git ls-files` failed\n")
99 sys.exit(2)
100 return [p for p in proc.stdout.split("\0") if p]
101
102
103def _present_files(files: list[str], root: Path | None = None) -> list[str]:
104 """Return candidate paths that still exist in the worktree."""
105 base = _repo_root() if root is None else root
106 return [rel for rel in files if (base / rel).is_file()]
107
108
109def _tracked_go_files() -> list[str]:
110 """Every candidate-worktree Go file, minus build-output trees."""
111 by_extension = _present_files(_git_ls_files("*.go"))
112 return sorted(rel for rel in by_extension if not is_build_output_path(rel))
113
114
115def _module_roots(files: list[str]) -> list[Path]:
116 """Distinct directories holding a go.mod above each of `files`."""
117 roots: set[Path] = set()
118 for rel in files:
119 directory = (_repo_root() / rel).parent
120 while directory != directory.parent:
121 if (directory / "go.mod").is_file():
122 roots.add(directory)
123 break
124 if directory == _repo_root():
125 break
126 directory = directory.parent
127 return sorted(roots)
128
129
130def _run_vet(go: str, roots: list[Path]) -> dict[str, str]:
131 """Run `go vet ./...` per module root; map root to stderr on failure."""
132 findings: dict[str, str] = {}
133 for root in roots:
134 proc = subprocess.run( # noqa: S603 -- fixed argv, trusted tool path
135 [go, "vet", "./..."],
136 cwd=root,
137 capture_output=True,
138 text=True,
139 check=False,
140 )
141 if proc.returncode != 0:
142 rel = (
143 str(root.relative_to(_repo_root()))
144 if root.is_relative_to(_repo_root())
145 else str(root)
146 )
147 findings[rel] = (proc.stdout + proc.stderr).strip()
148 return findings
149
150
151def _run_staticcheck(staticcheck: str, roots: list[Path]) -> dict[str, str]:
152 """Run `staticcheck ./...` per module root; map root to output on failure."""
153 findings: dict[str, str] = {}
154 for root in roots:
155 proc = subprocess.run( # noqa: S603 -- fixed argv, trusted tool path
156 [staticcheck, "./..."],
157 cwd=root,
158 capture_output=True,
159 text=True,
160 check=False,
161 )
162 if proc.returncode != 0:
163 rel = (
164 str(root.relative_to(_repo_root()))
165 if root.is_relative_to(_repo_root())
166 else str(root)
167 )
168 findings[rel] = (proc.stdout + proc.stderr).strip()
169 return findings
170
171
172def _run_tests(go: str, roots: list[Path]) -> dict[str, str]:
173 failures: dict[str, str] = {}
174 env = os.environ.copy()
175 env["GOWORK"] = "off"
176 for root in roots:
177 proc = subprocess.run( # noqa: S603 -- fixed argv, trusted tool path
178 [go, "test", "-race", "-v", "./..."],
179 cwd=root,
180 env=env,
181 capture_output=True,
182 text=True,
183 check=False,
184 )
185 if proc.returncode != 0:
186 rel = (
187 str(root.relative_to(_repo_root()))
188 if root.is_relative_to(_repo_root())
189 else str(root)
190 )
191 failures[rel] = (proc.stdout + proc.stderr).strip()
192 return failures
193
194
195def _parse_coverage(stdout: str) -> list[tuple[str, float, bool]]:
196 """Parse (package_or_scope, coverage_pct, is_module_wide) from `go test -cover` output."""
197 results: list[tuple[str, float, bool]] = []
198 for raw in stdout.splitlines():
199 line = raw.strip()
200 match = re.search(r"coverage:\s+([0-9]+(?:\.[0-9]+)?)\%\s+of\s+statements", line)
201 if not match:
202 continue
203 pct = float(match.group(1))
204 # Non-test packages output `\t<pkg>\t\tcoverage: 0.0% of statements` under -coverpkg
205 if not raw.startswith(("ok\t", "ok ")) and pct == 0.0:
206 continue
207 parts = line.split()
208 pkg = parts[1] if len(parts) > 1 and parts[0] in ("ok", "FAIL") else parts[0]
209 is_module_wide = " in ./..." in line
210 results.append((pkg, pct, is_module_wide))
211 return results
212
213
214def _run_coverage(
215 go: str, roots: list[Path], floor: float
216) -> tuple[dict[str, str], dict[str, str]]:
217 """Run `go test -cover` across module roots, verifying coverage >= floor."""
218 successes: dict[str, str] = {}
219 failures: dict[str, str] = {}
220 env = os.environ.copy()
221 env["GOWORK"] = "off"
222 for root in roots:
223 rel = (
224 str(root.relative_to(_repo_root())) if root.is_relative_to(_repo_root()) else str(root)
225 )
226 proc = subprocess.run( # noqa: S603 -- fixed argv, trusted tool path
227 [go, "test", "-cover", "-coverpkg=./...", "./..."],
228 cwd=root,
229 env=env,
230 capture_output=True,
231 text=True,
232 check=False,
233 )
234 if proc.returncode != 0:
235 failures[rel] = (proc.stdout + proc.stderr).strip()
236 continue
237
238 results = _parse_coverage(proc.stdout)
239 if not results:
240 failures[rel] = "no statement coverage reported (0 tests or statements executed)"
241 continue
242
243 module_wide = [pct for (_, pct, is_mod) in results if is_mod]
244 if module_wide:
245 effective_pct = max(module_wide)
246 if effective_pct < floor:
247 failures[rel] = (
248 f"coverage {effective_pct:.1f}% is below floor {floor:.1f}% "
249 f"(target: {floor:.1f}%)"
250 )
251 else:
252 successes[rel] = f"coverage {effective_pct:.1f}% (floor {floor:.1f}%)"
253 else:
254 below_floor = [(pkg, pct) for (pkg, pct, _) in results if pct < floor]
255 if below_floor:
256 details = ", ".join(f"{pkg}: {pct:.1f}%" for pkg, pct in below_floor)
257 failures[rel] = f"package(s) below floor {floor:.1f}%: {details}"
258 else:
259 min_pct = min(pct for (_, pct, _) in results)
260 successes[rel] = f"coverage {min_pct:.1f}% (floor {floor:.1f}%)"
261
262 return successes, failures
263
264
265def _parse_floor(args: list[str]) -> float:
266 """Parse --floor <pct> or --floor=<pct>, defaulting to 85.0."""
267 default_floor = 85.0
268 for i, arg in enumerate(args):
269 if arg == "--floor":
270 if i + 1 < len(args):
271 try:
272 return float(args[i + 1])
273 except ValueError:
274 sys.stderr.write(f"check_go.py: invalid --floor value: {args[i + 1]}\n")
275 sys.exit(2)
276 else:
277 sys.stderr.write("check_go.py: --floor requires a numeric argument\n")
278 sys.exit(2)
279 elif arg.startswith("--floor="):
280 val = arg.split("=", 1)[1]
281 try:
282 return float(val)
283 except ValueError:
284 sys.stderr.write(f"check_go.py: invalid --floor value: {val}\n")
285 sys.exit(2)
286 return default_floor
287
288
289def _report(
290 vet: dict[str, str],
291 staticcheck: dict[str, str] | None = None,
292) -> None:
293 if vet:
294 sys.stderr.write("check_go.py: `go vet` finding(s):\n")
295 for relroot in sorted(vet):
296 sys.stderr.write(f" {relroot}:\n")
297 for line in vet[relroot].splitlines():
298 sys.stderr.write(f" {line}\n")
299 if staticcheck:
300 sys.stderr.write("check_go.py: `staticcheck` finding(s):\n")
301 for relroot in sorted(staticcheck):
302 sys.stderr.write(f" {relroot}:\n")
303 for line in staticcheck[relroot].splitlines():
304 sys.stderr.write(f" {line}\n")
305 sys.stderr.write("\nFix the finding.\n")
306
307
308# ---------------------------------------------------------------------------
309# Selftest
310#
311# Fixtures live in throwaway directories, never in the tree: a deliberately
312# non-conforming .go file stored as a real file would be picked up by the
313# gate's own scan and fail it.
314# ---------------------------------------------------------------------------
315
316
317def _vet_fails(go: str, source: str) -> bool:
318 """True when `go vet` rejects a scratch module holding `source`."""
319 with tempfile.TemporaryDirectory() as tmp:
320 root = Path(tmp)
321 (root / "go.mod").write_text("module selftest\n\ngo 1.24\n", encoding="utf-8")
322 (root / "fixture.go").write_text(source, encoding="utf-8")
323 proc = subprocess.run( # noqa: S603 -- fixed argv, trusted tool path
324 [go, "vet", "./..."],
325 cwd=root,
326 capture_output=True,
327 text=True,
328 check=False,
329 )
330 return proc.returncode != 0
331
332
333def _staticcheck_fails(staticcheck: str, source: str) -> bool:
334 """True when `staticcheck ./...` rejects a scratch module holding `source`."""
335 with tempfile.TemporaryDirectory() as tmp:
336 root = Path(tmp)
337 (root / "go.mod").write_text("module selftest\n\ngo 1.24\n", encoding="utf-8")
338 (root / "fixture.go").write_text(source, encoding="utf-8")
339 proc = subprocess.run( # noqa: S603 -- fixed argv, trusted tool path
340 [staticcheck, "./..."],
341 cwd=root,
342 capture_output=True,
343 text=True,
344 check=False,
345 )
346 return proc.returncode != 0
347
348
349def _selftest_vet(go: str, failures: list[str]) -> None:
350 vet_bad = 'package selftest\n\nfunc f() int {\n\treturn "not an int"\n}\n'
351 vet_good = "package selftest\n\nfunc f() int {\n\treturn 1\n}\n"
352 if not _vet_fails(go, vet_bad):
353 failures.append(" must-fire: `go vet` accepted a mistyped return")
354 if _vet_fails(go, vet_good):
355 failures.append(" must-stay-quiet: `go vet` rejected the clean fixture")
356
357
358def _selftest_staticcheck_fn(staticcheck: str, failures: list[str]) -> None:
359 sc_bad = "package selftest\n\nfunc dead() {\n\treturn\n\tprintln(1)\n}\n"
360 sc_good = (
361 "package selftest\n\n// Add returns sum.\nfunc Add(a, b int) int {\n\treturn a + b\n}\n"
362 )
363 if not _staticcheck_fails(staticcheck, sc_bad):
364 failures.append(" must-fire: `staticcheck` accepted dead code / unused function")
365 if _staticcheck_fails(staticcheck, sc_good):
366 failures.append(" must-stay-quiet: `staticcheck` rejected clean exported function")
367
368
369def _selftest_tests(go: str, root: Path, failures: list[str]) -> None:
370 root = root.resolve()
371 (root / "go.mod").write_text("module testrun\n\ngo 1.24\n", encoding="utf-8")
372 (root / "calc.go").write_text(
373 "package testrun\n\nfunc Add(a, b int) int { return a + b }\n",
374 encoding="utf-8",
375 )
376 (root / "calc_test.go").write_text(
377 'package testrun\n\nimport "testing"\n\nfunc TestAdd(t *testing.T) {\n\tif Add(1, 2) != 3 { t.Fatal("fail") }\n}\n', # noqa: E501
378 encoding="utf-8",
379 )
380 pass_findings = _run_tests(go, [root])
381 if pass_findings:
382 failures.append(f" must-stay-quiet: `_run_tests` flagged a passing test: {pass_findings}")
383
384 (root / "fail_test.go").write_text(
385 'package testrun\n\nimport "testing"\n\nfunc TestBoom(t *testing.T) {\n\tt.Fatal("boom")\n}\n', # noqa: E501
386 encoding="utf-8",
387 )
388 fail_findings = _run_tests(go, [root])
389 if not fail_findings:
390 failures.append(" must-fire: `_run_tests` did not report a failing test")
391 (root / "fail_test.go").unlink()
392
393 # Data race check
394 (root / "race_test.go").write_text(
395 """package testrun
396
397import "testing"
398
399func TestDataRace(t *testing.T) {
400 var x int
401 ch := make(chan struct{})
402 go func() {
403 x = 1
404 close(ch)
405 }()
406 x = 2
407 <-ch
408 _ = x
409}
410""",
411 encoding="utf-8",
412 )
413 race_findings = _run_tests(go, [root])
414 if not race_findings:
415 failures.append(" must-fire: `_run_tests` (-race) did not detect a data race")
416 (root / "race_test.go").unlink()
417
418
419def _selftest_coverage(go: str, root: Path, failures: list[str]) -> None:
420 root = root.resolve()
421 (root / "go.mod").write_text("module testcov\n\ngo 1.24\n", encoding="utf-8")
422 (root / "branch.go").write_text(
423 """package testcov
424
425func Branch(x int) int {
426 if x > 0 {
427 return 1
428 }
429 return -1
430}
431""",
432 encoding="utf-8",
433 )
434 (root / "branch_test.go").write_text(
435 """package testcov
436
437import "testing"
438
439func TestBranch(t *testing.T) {
440 if Branch(1) != 1 {
441 t.Fatal("fail")
442 }
443}
444""",
445 encoding="utf-8",
446 )
447 _, cov_fail_85 = _run_coverage(go, [root], floor=85.0)
448 if not cov_fail_85:
449 failures.append(" must-fire: `_run_coverage` passed 66.7% coverage at floor 85.0%")
450 cov_succ_50, cov_fail_50 = _run_coverage(go, [root], floor=50.0)
451 if cov_fail_50:
452 failures.append(" must-stay-quiet: `_run_coverage` failed 66.7% coverage at floor 50.0%")
453 if not cov_succ_50:
454 failures.append(" must-record: `_run_coverage` did not record success at floor 50.0%")
455
456
457def selftest(go: str, staticcheck: str | None = None) -> int:
458 """Prove all tools fire where they must and stay quiet where they must."""
459 failures: list[str] = []
460
461 _selftest_vet(go, failures)
462 if staticcheck:
463 _selftest_staticcheck_fn(staticcheck, failures)
464
465 with tempfile.TemporaryDirectory() as tmp:
466 root = Path(tmp)
467 _selftest_tests(go, root, failures)
468
469 with tempfile.TemporaryDirectory() as tmp:
470 root = Path(tmp)
471 _selftest_coverage(go, root, failures)
472 # Unit checks for floor and coverage parsing
473 if _parse_floor([]) != 85.0: # noqa: PLR2004
474 failures.append(" _parse_floor default was not 85.0")
475 if _parse_floor(["--floor", "72.5"]) != 72.5: # noqa: PLR2004
476 failures.append(" _parse_floor did not parse --floor 72.5")
477 if _parse_floor(["--floor=91.0"]) != 91.0: # noqa: PLR2004
478 failures.append(" _parse_floor did not parse --floor=91.0")
479
480 parsed_wide = _parse_coverage("ok pkg/tests 0.1s coverage: 88.5% of statements in ./...")
481 if parsed_wide != [("pkg/tests", 88.5, True)]:
482 failures.append(" _parse_coverage failed on module-wide output")
483
484 parsed_blank = _parse_coverage("\tpkg\t\tcoverage: 0.0% of statements")
485 if parsed_blank:
486 failures.append(" _parse_coverage did not ignore non-test 0.0% package line")
487
488 # Scope check
489 with tempfile.TemporaryDirectory() as tmp:
490 fixture_root = Path(tmp)
491 (fixture_root / "present.go").touch()
492 present = _present_files(["present.go", "deleted.go"], fixture_root)
493 if present != ["present.go"]:
494 failures.append(" worktree scope did not exclude exactly the deleted fixture")
495
496 if failures:
497 sys.stderr.write("check_go.py --selftest: FAILED\n")
498 sys.stderr.write("\n".join(failures) + "\n")
499 return 1
500
501 sc_str = "staticcheck, " if staticcheck else ""
502 print(f"check_go.py --selftest: OK (vet, {sc_str}test, race, coverage, floor).")
503 return 0
504
505
506def _lint_phase(go: str, roots: list[Path]) -> tuple[dict[str, str], dict[str, str], str | None]:
507 """Run vet and staticcheck over `roots`."""
508 staticcheck_bin = _find_staticcheck()
509 vet_findings = _run_vet(go, roots)
510 staticcheck_findings = _run_staticcheck(staticcheck_bin, roots) if staticcheck_bin else {}
511 return vet_findings, staticcheck_findings, staticcheck_bin
512
513
514def _test_phase(go: str, roots: list[Path], do_test: bool) -> dict[str, str]:
515 """Run `go test -race` on every root; report and return any failures."""
516 if not do_test:
517 return {}
518 test_failures = _run_tests(go, roots)
519 if test_failures:
520 sys.stderr.write("check_go.py: `go test -race` failure(s):\n")
521 for relroot in sorted(test_failures):
522 sys.stderr.write(f" {relroot}:\n")
523 for line in test_failures[relroot].splitlines():
524 sys.stderr.write(f" {line}\n")
525 return test_failures
526
527
528def _coverage_phase(
529 go: str, roots: list[Path], floor: float, do_coverage: bool
530) -> tuple[dict[str, str], dict[str, str]]:
531 """Run coverage against `floor`; report and return (successes, failures)."""
532 if not do_coverage:
533 return {}, {}
534 cov_successes, cov_failures = _run_coverage(go, roots, floor)
535 if cov_failures:
536 sys.stderr.write("check_go.py: coverage failure(s):\n")
537 for relroot in sorted(cov_failures):
538 sys.stderr.write(f" {relroot}: {cov_failures[relroot]}\n")
539 return cov_successes, cov_failures
540
541
542def _go_or_exit(go: str | None, args: list[str]) -> str:
543 """Resolve the Go binary, exiting when absent unless a hook may skip."""
544 if not go:
545 msg = "check_go.py: go not found"
546 if "--require" in args or "--selftest" in args:
547 sys.stderr.write(msg + " -- required by --require/--selftest\n")
548 sys.exit(1)
549 print(msg + " -- skipping (install Go to enforce locally).")
550 sys.exit(0)
551 return go
552
553
554def _scope_or_error(tracked: list[str]) -> int | None:
555 """Return an exit code when the Go scope collapsed below the file floor."""
556 file_floor = 3
557 if len(tracked) >= file_floor:
558 return None
559 sys.stderr.write(
560 f"check_go.py: FATAL -- only {len(tracked)} Go file(s) in scope, "
561 f"floor is {file_floor}.\n"
562 " A collapsed scope reports a clean tree because it checked nothing.\n"
563 )
564 return 2
565
566
567def _render_summary(
568 tracked: list[str],
569 roots: list[Path],
570 mode: str,
571 staticcheck_bin: str | None,
572 cov_successes: dict[str, str],
573) -> None:
574 """Print the per-mode clean summary on stdio."""
575 summary_parts: list[str] = []
576 if "lint" in mode:
577 sc_desc = "vet/staticcheck" if staticcheck_bin else "vet"
578 summary_parts.append(f"{sc_desc} clean")
579 if "test" in mode:
580 summary_parts.append("tests passed (race detector clean)")
581 if "coverage" in mode:
582 cov_str = ", ".join(f"{k}: {v}" for k, v in sorted(cov_successes.items()))
583 summary_parts.append(f"{cov_str}")
584 print(
585 f"check_go.py: clean ({len(tracked)} files, {len(roots)} module(s), "
586 f"{'; '.join(summary_parts)})."
587 )
588
589
590def _execute_lint_or_test(go: str, tracked: list[str], roots: list[Path], args: list[str]) -> int:
591 """Run lint/test/coverage, returning the process exit code directly."""
592 do_test = "--test" in args
593 do_coverage = "--coverage" in args
594 do_lint = "--lint" in args or (not do_test and not do_coverage)
595 floor = _parse_floor(args)
596
597 if do_lint:
598 vet_findings, staticcheck_findings, staticcheck_bin = _lint_phase(go, roots)
599 if vet_findings or staticcheck_findings:
600 _report(vet_findings, staticcheck_findings)
601 return 1
602 if not do_test and not do_coverage:
603 sc_note = ", staticcheck" if staticcheck_bin else ""
604 print(f"check_go.py: clean ({len(tracked)} files, no vet{sc_note} findings).")
605 return 0
606 else:
607 vet_findings = {}
608 staticcheck_findings = {}
609 staticcheck_bin = None
610
611 test_failures = _test_phase(go, roots, do_test)
612 cov_successes, cov_failures = _coverage_phase(go, roots, floor, do_coverage)
613
614 if test_failures or cov_failures:
615 return 1
616
617 parts = ("lint", "test", "coverage")
618 on = (do_lint, do_test, do_coverage)
619 mode = "".join(part for part, flag in zip(parts, on, strict=True) if flag)
620 _render_summary(tracked, roots, mode, staticcheck_bin, cov_successes)
621 return 0
622
623
624def _selftest_or_scope(go: str, args: list[str]) -> int | None:
625 """Run the selftest or return an error exit for a collapsed scope."""
626 if "--selftest" in args:
627 staticcheck = _find_staticcheck()
628 return selftest(go, staticcheck)
629 tracked = _tracked_go_files()
630 scope_error = _scope_or_error(tracked)
631 if scope_error is not None:
632 return scope_error
633 return None
634
635
636def _gate_main(go: str, args: list[str]) -> int:
637 """Dispatch the selftest or the lint/test/coverage phases for present Go."""
638 early = _selftest_or_scope(go, args)
639 if early is not None:
640 return early
641 tracked = _tracked_go_files()
642 return _execute_lint_or_test(go, tracked, _module_roots(tracked), args)
643
644
645def main(argv: list[str]) -> int:
646 """Run Go verification over every tracked first-party Go file.
647
648 A missing Go toolchain is handled two ways ON PURPOSE, mirroring
649 check_ruff.py. Bare, it prints a notice and exits 0, so a contributor
650 without Go is not blocked by a local hook. Under ``--require`` or
651 ``--selftest`` it exits 1 instead -- CI passes ``--require`` precisely so
652 that an absent toolchain fails the build rather than skipping silently.
653
654 ``--list-files`` exists for check_lint_coverage.py and needs no toolchain:
655 it reports exactly the files this gate would check.
656
657 Returns 0 when clean, 1 on any finding or test/coverage failure, 2 on tool
658 error or collapsed scope.
659 """
660 args = argv[1:]
661
662 if "--list-files" in args:
663 # No toolchain needed: coverage asks what would be scanned, and the
664 # answer must not depend on whether go is installed.
665 print("\n".join(_tracked_go_files()))
666 return 0
667
668 go = _go_or_exit(_find_go(), args)
669 return _gate_main(go, args)
670
671
672if __name__ == "__main__":
673 sys.exit(main(sys.argv))
void main(void)
The application entry point Reset_Handler hands control to.
Definition main.c:298
#define min(x, y)
Untyped minimum shim used by the SOUP's buffer clamping.
Definition xz_config.h:157