6f1ba1fe43
gates / gates (push) Successful in 49s
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
77 lines
3.1 KiB
Python
77 lines
3.1 KiB
Python
#!/usr/bin/env python3
|
|
"""gofmt_gate.py — every Go file in this module is gofmt-clean (R-454).
|
|
|
|
MEASURED 2026-09-02: five internal/web test files had been gofmt-unclean for an unknown length of
|
|
time and nothing noticed — `go vet` does not check formatting and no gate did. By 2026-10-05 the five
|
|
were clean and TWELVE others were not: the count only grows while nothing looks, and every `gofmt -l`
|
|
someone runs by hand is then noise that hides a real one.
|
|
|
|
Run from controller/: python3 scripts/gofmt_gate.py
|
|
Exit 0 clean · 1 convicted (lists the files) · 2 inconclusive (gofmt not found — never a pass).
|
|
|
|
gofmt is the Go toolchain's own formatter (not a shell utility), found on PATH or under
|
|
`go env GOROOT`/bin. Decoy: scripts/test_gate_decoys.py ("gofmt").
|
|
"""
|
|
import os
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
|
|
CTRL = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
ROOTS = ["cmd", "internal"]
|
|
|
|
|
|
def find_gofmt():
|
|
p = shutil.which("gofmt")
|
|
if p:
|
|
return p
|
|
go = shutil.which("go")
|
|
if go:
|
|
try:
|
|
root = subprocess.check_output([go, "env", "GOROOT"], text=True).strip()
|
|
cand = os.path.join(root, "bin", "gofmt")
|
|
if os.path.isfile(cand):
|
|
return cand
|
|
except Exception:
|
|
pass
|
|
return None
|
|
|
|
|
|
def in_ci():
|
|
"""True on the Gitea Actions runner (act sets GITHUB_ACTIONS, Gitea sets GITEA_ACTIONS)."""
|
|
return os.environ.get("GITEA_ACTIONS") == "true" or os.environ.get("GITHUB_ACTIONS") == "true"
|
|
|
|
|
|
def main():
|
|
gofmt = find_gofmt()
|
|
if not gofmt:
|
|
# The CI runner is Alpine + python3 + git — no Go toolchain (felhom.eu CLAUDE.md, R-887 note). There the gate
|
|
# says so OUT LOUD and passes: the pre-push hook on the developer's machine, where Go always exists, is where
|
|
# formatting is enforced. Anywhere else a missing gofmt stays INCONCLUSIVE — never a silent pass.
|
|
# Pinned by the "gofmt/ci-without-go" and "gofmt/dev-without-go" decoys in test_gate_decoys.py.
|
|
if in_ci():
|
|
print("gofmt gate NOT CHECKED in CI: no Go toolchain on the runner — the pre-push hook (with Go) enforces it")
|
|
return 0
|
|
print("gofmt gate INCONCLUSIVE: gofmt not found on PATH or under `go env GOROOT`/bin")
|
|
return 2
|
|
roots = [r for r in ROOTS if os.path.isdir(os.path.join(CTRL, r))]
|
|
if not roots:
|
|
print("gofmt gate INCONCLUSIVE: none of %s exists under %s" % (ROOTS, CTRL))
|
|
return 2
|
|
p = subprocess.run([gofmt, "-l"] + roots, cwd=CTRL, capture_output=True, text=True)
|
|
if p.returncode != 0:
|
|
print("gofmt gate INCONCLUSIVE: gofmt exited %d: %s" % (p.returncode, p.stderr.strip()[:400]))
|
|
return 2
|
|
bad = [l for l in p.stdout.splitlines() if l.strip()]
|
|
if bad:
|
|
for f in bad:
|
|
print(" not gofmt-clean: %s" % f)
|
|
print("GOFMT GATE FAILED: %d file(s) — run `gofmt -w <file>` (formatting only, no behaviour change)" % len(bad))
|
|
return 1
|
|
print("gofmt gate OK — every Go file under %s is gofmt-clean" % ", ".join(roots))
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|