From 6f1ba1fe43f2114838b3c4f8c4aa6cf79350bbe6 Mon Sep 17 00:00:00 2001 From: kisfenyo Date: Mon, 5 Oct 2026 20:15:54 +0200 Subject: [PATCH] gofmt gate: NOT CHECKED (out loud) on the Go-less CI runner, INCONCLUSIVE elsewhere; decoys for both (CI run 1371 was red) Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS --- CHANGELOG.md | 4 +++- controller/scripts/gofmt_gate.py | 12 ++++++++++++ controller/scripts/test_gate_decoys.py | 25 ++++++++++++++++++++++++- 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dc13673..b31c72f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,7 +21,9 @@ a future-tense or reminder sentence. - **R-425, R-564, R-565, R-603:** the off-site rename gate finds files by pattern; the retrieval-promise gate knows split-verb Hungarian; the English-page test knows ASCII Hungarian words; a Go-side check for HTML-escapable values. -- **R-454:** `scripts/gofmt_gate.py` (registered, with a decoy); 12 files formatted. **R-208:** `ARG VERSION`/`GIT_COMMIT` +- **R-454:** `scripts/gofmt_gate.py` (registered, with decoys); 12 files formatted. On the CI runner (no Go) it says + „NOT CHECKED in CI" and passes; elsewhere a missing gofmt stays INCONCLUSIVE (the first push of this release was red + in CI for exactly that — run 1371). **R-208:** `ARG VERSION`/`GIT_COMMIT` sit just above `go build`. **R-457:** swept, no date literal feeds a clock assertion — no change. Red-proofs: `felhom.eu/documentation/audits/burndown2-2026-10-05/controller-red-proofs.txt` (two first attempts that did diff --git a/controller/scripts/gofmt_gate.py b/controller/scripts/gofmt_gate.py index 2ea5575..9620f69 100644 --- a/controller/scripts/gofmt_gate.py +++ b/controller/scripts/gofmt_gate.py @@ -37,9 +37,21 @@ def find_gofmt(): 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))] diff --git a/controller/scripts/test_gate_decoys.py b/controller/scripts/test_gate_decoys.py index 3734900..90d210b 100644 --- a/controller/scripts/test_gate_decoys.py +++ b/controller/scripts/test_gate_decoys.py @@ -213,11 +213,34 @@ try: _rc, _out = gate("gofmt_gate.py") finally: os.remove(_gf) -if _rc != 1 or "zz_gofmt_decoy_tmp.go" not in _out: +if "NOT CHECKED in CI" in _out: + print(" -- %-20s not runnable here (CI, no Go toolchain) — the two cases below pin that mode" % "gofmt") +elif _rc != 1 or "zz_gofmt_decoy_tmp.go" not in _out: fails.append("gofmt: an unformatted planted file was not convicted (rc=%d)\n%s" % (_rc, _out[-400:])) else: print(" ok %-20s decoy rejected" % "gofmt") +# The gate's two no-Go modes (2026-10-05): with no gofmt reachable, CI passes OUT LOUD and a dev machine is +# INCONCLUSIVE. PATH is emptied of Go for both; only the CI marker differs. +import tempfile as _tf +_EMPTY_PATH_DIR = _tf.mkdtemp(prefix="nogo-") +def _gofmt_without_go(ci): + env = {k: v for k, v in os.environ.items() if k not in ("GITEA_ACTIONS", "GITHUB_ACTIONS", "GOROOT")} + env["PATH"] = _EMPTY_PATH_DIR # nothing on it: sys.executable is called by its full path + if ci: + env["GITEA_ACTIONS"] = "true" + p = subprocess.run([sys.executable, os.path.join("scripts", "gofmt_gate.py")], cwd=CTRL, env=env, + capture_output=True, text=True) + return p.returncode, p.stdout + p.stderr +for _ci, _want_rc, _want_txt, _name in ((True, 0, "NOT CHECKED in CI", "gofmt/ci-without-go"), + (False, 2, "INCONCLUSIVE", "gofmt/dev-without-go")): + ran += 1 + _rc, _out = _gofmt_without_go(_ci) + if _rc != _want_rc or _want_txt not in _out: + fails.append("%s: want rc=%d and %r, got rc=%d\n%s" % (_name, _want_rc, _want_txt, _rc, _out[-300:])) + else: + print(" ok %-20s %s" % (_name, "passes out loud" if _ci else "stays undetermined")) + # --- go-parity (v0.252.0, R-557): a Go-side message key may only carry base-commit text. --- # --- Three shapes, because the gate makes three different claims. --- GO_KEYS = os.path.join(HERE, "i18n_go_keys.json")