Watch
1
0
Fork
You've already forked SouveraineOS
0
SouveraineOS/tools/claude-hook-push-ci.sh
Fimeg f7b4b04525 tools: read CI outcomes from the API, and do it automatically
A rotted action pin skipped the publish job rather than failing it, so
edge stopped publishing and the phone sat on an old build for a working
day with nothing saying why. The runner journal logs task pickup and
never outcome, which is what was being read.

ci-status.sh asks Gitea instead, drilling run -> job -> log tail on a
failure. claude-hook-push-ci.sh runs it after every git push so the
answer arrives without anyone choosing to look. TASK-42 has the
three-layer plan; this is layer 1.
2026-07-28 17:38:44 -04:00

50 lines
1.9 KiB
Shell
Executable file

#!/usr/bin/env bash
# Claude Code PostToolUse hook: after a `git push`, say what CI did.
#
# Wired in ~/.claude/settings.json as a PostToolUse hook on Bash. Reads the
# tool-call JSON on stdin, does nothing unless the command was a push, and
# otherwise prints the pushed repo's CI status so the answer arrives without
# anyone deciding to ask for it.
#
# WHY. A pinned action SHA rotted on 2026-07-28 and stopped `edge` publishing
# for a working day. Nothing failed loudly — the publish job was *skipped* —
# and the habit in place was to read the runner journal, which reports task
# pickup and never outcome. Remembering to check is not a mechanism; this is.
#
# WHAT IT CANNOT DO. A cross build takes ~20 minutes, so immediately after a
# push the honest answer is usually "running". That still catches the whole
# fail-fast class — pin rot, lint, attribution, a bad workflow edit — which is
# what actually bites. Anything slower needs the layer-2 staleness check in
# TASK-42, not a longer sleep here.
#
# Never fails the tool call: exit 0 always. A status reporter that can block a
# push is a worse problem than the one it solves.
set -uo pipefail
SELF_DIR="$(dirname "$(realpath "$0")")"
payload=$(cat 2>/dev/null || true)
cmd=$(printf '%s' "$payload" | python3 -c '
import json, sys
try:
print(json.load(sys.stdin).get("tool_input", {}).get("command", ""))
except Exception:
pass
' 2>/dev/null)
case "$cmd" in
*"git push"*) ;;
*) exit 0 ;;
esac
# Which repo. The push may have run in any directory, so prefer the cwd's
# remote and fall back to whatever the command named.
repo=""
url=$(git remote -v 2>/dev/null | awk 'NR==1{print $2}')
[ -n "$url" ] && repo=$(basename "${url%.git}")
[ -n "$repo" ] || exit 0
echo "--- CI (auto, after git push) ---"
"$SELF_DIR/ci-status.sh" "$repo" 2 2>&1 | head -30
echo "--- a build in progress needs a second look; see TASK-42 ---"
exit 0