From e7ed31b4e16b91cb69d78e91bf82052befade273 Mon Sep 17 00:00:00 2001 From: reachableceo Date: Fri, 4 Sep 2026 07:17:01 -0500 Subject: [PATCH] =?UTF-8?q?feat(hooks):=20CHANGE=20GATE=20=E2=80=94=20prod?= =?UTF-8?q?=20remote=20ops=20require=20.crush/active-cr=20[#767]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Port of the PFVCluster gate to the global PreToolUse copy (all lanes): prod-target remote.sh/remote-dns.sh invocations block without a filed GLPI Change id (.crush/active-cr); dev-lane targets exempt; CR tooling exempt. JSON error payload per house style. Functionally verified. https://projects.knownelement.com/issues/767#note-4191 --- hooks/global/ticket-gate.sh | 39 ++++++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/hooks/global/ticket-gate.sh b/hooks/global/ticket-gate.sh index 550f34f..9e9bc2b 100755 --- a/hooks/global/ticket-gate.sh +++ b/hooks/global/ticket-gate.sh @@ -37,6 +37,7 @@ if [ "$TOOL" = "bash" ]; then *"check-rules"*|*"setup-hooks"*|*"shellcheck"*|*"run-tests"*|*"promote.sh"*) exit 0 ;; *"tailscale status"*|*"access-matrix"*|*"docker ps"*|*"docker logs"*|*"docker inspect"*) exit 0 ;; *active-ticket*) exit 0 ;; + *active-cr*|*"glpi-change"*) exit 0 ;; esac fi @@ -52,10 +53,38 @@ TICKET_FILE="$REPO_ROOT/.crush/active-ticket" if [ -f "$TICKET_FILE" ] && [ -s "$TICKET_FILE" ]; then TICKET="$(tr -d '\n' < "$TICKET_FILE")" printf '{"context":"Active ticket: %s"}\n' "$TICKET" - exit 0 -fi - -cat >&2 <<'EOF' +else + cat >&2 <<'EOF' {"error":{"message":"TICKET GATE: no active ticket in this repo. Set one first: echo '#NNN' > .crush/active-ticket (create the ticket in Redmine first if none exists). Clear with: > .crush/active-ticket"}} EOF -exit 2 + exit 2 +fi + +# CHANGE GATE [#767] (t/325 §5 ladder; Q3 ruling 2026-09-04): prod-target +# remote operations additionally require a filed GLPI Change — its id in +# .crush/active-cr (clear when the CR closes: > .crush/active-cr) +# Dev-lane targets are exempt (sectestbed/preprod/test/sandbox = Tier 0/1). +# CR tooling: KNEL/inventory scripts/glpi-change.sh (create --agent). +if [ "$TOOL" = "bash" ]; then + case "$CMD" in + *"remote.sh vm-file"*|*"remote.sh prox-file"*|*"remote.sh vm-copy"*|*"remote.sh prox-copy"*|*"remote.sh vm"*|*"remote.sh prox"*|*"remote-dns.sh "*) + PROD=1 + case "$CMD" in + *sectestbed*|*preprod*|*glpi-test*|*kali*|*tsys5*|*sandbox*) PROD=0 ;; + esac + if [ "$PROD" -eq 1 ]; then + CR_FILE="$REPO_ROOT/.crush/active-cr" + if [ -f "$CR_FILE" ] && [ -s "$CR_FILE" ]; then + printf '{"context":"Active CR: %s"}\n' "$(tr -d '\n' < "$CR_FILE")" + else + cat >&2 <<'EOF' +{"error":{"message":"CHANGE GATE: prod-target remote operation requires a filed GLPI Change. File one: echo 'what/why/where' | scripts/glpi-change.sh create --agent --title '[#NNN] summary' — then: echo '' > .crush/active-cr. Dev-lane targets (sectestbed/preprod/test/tsys5) are exempt. Clear with: > .crush/active-cr"}} +EOF + exit 2 + fi + fi + ;; + esac +fi + +exit 0