Commit 7a019c7

Eric Bower  ·  2026-05-07 15:38:52 -0400 EDT
parent bcba2bc
feat: git post-receive hook
2 files changed,  +124, -0
A hooks/post-receive
+31, -0
 1@@ -0,0 +1,31 @@
 2+#!/usr/bin/env bash
 3+# post-receive hook — publishes build events to pici via ssh pipe.
 4+#
 5+# Install: ./install-hooks.sh /path/to/bare-repos
 6+#
 7+# stdin format (per ref pushed): <old-sha> <new-sha> <ref-name>
 8+
 9+set -euo pipefail
10+
11+PIPE_HOST="${PIPE_HOST:-pipe}"
12+
13+while read -r old_sha new_sha ref; do
14+    # Skip delete refs
15+    if [ "$new_sha" = "0000000000000000000000000000000000000000" ]; then
16+        continue
17+    fi
18+
19+    # Extract branch name from ref (e.g. refs/heads/main → main)
20+    branch="${ref#refs/heads/}"
21+
22+    # Repo name from the bare repo directory
23+    repo="$(basename "$(git rev-parse --show-toplevel 2>/dev/null || pwd)" .git)"
24+
25+    # Workspace is the bare repo itself (runner rsyncs from here)
26+    workspace="$(git rev-parse --git-dir 2>/dev/null || pwd)"
27+
28+    event=$(printf '{"type":"git.push","name":"%s","workspace":"%s","branch":"%s","commit":"%s"}' \
29+        "$repo" "$workspace" "$branch" "$new_sha")
30+
31+    echo "$event" | ssh "$PIPE_HOST" pub -b=false build.event
32+done
A install-hooks.sh
+93, -0
 1@@ -0,0 +1,93 @@
 2+#!/usr/bin/env bash
 3+# install-hooks.sh — sync the post-receive hook to all bare repos in a directory.
 4+#
 5+# Usage:
 6+#   ./install-hooks.sh /path/to/bare-repos
 7+#   ./install-hooks.sh /path/to/repo1.git /path/to/repo2.git
 8+#   ./install-hooks.sh --force /path/to/bare-repos    # overwrite existing hooks
 9+#
10+# The hook is read from hooks/post-receive relative to this script.
11+# Errors if a repo already has a different post-receive hook (use --force to overwrite).
12+
13+set -euo pipefail
14+
15+SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
16+HOOK="$SCRIPT_DIR/hooks/post-receive"
17+FORCE=false
18+
19+if [ "${1:-}" = "--force" ]; then
20+    FORCE=true
21+    shift
22+fi
23+
24+if [ ! -f "$HOOK" ]; then
25+    echo "error: $HOOK not found" >&2
26+    exit 1
27+fi
28+
29+if [ $# -eq 0 ]; then
30+    echo "usage: $0 [--force] <repo.git ... | repos-dir>" >&2
31+    exit 1
32+fi
33+
34+repos=()
35+for arg in "$@"; do
36+    # Strip trailing slash to avoid double slashes in paths
37+    arg="${arg%/}"
38+    if [ -d "$arg" ] && [ ! -d "$arg/hooks" ]; then
39+        # Looks like a directory containing repos, not a repo itself
40+        for repo in "$arg"/*.git; do
41+            [ -d "$repo" ] && repos+=("$repo")
42+        done
43+    else
44+        repos+=("$arg")
45+    fi
46+done
47+
48+if [ ${#repos[@]} -eq 0 ]; then
49+    echo "no repos found" >&2
50+    exit 1
51+fi
52+
53+installed=0
54+skipped=0
55+overwritten=0
56+errors=0
57+
58+for repo in "${repos[@]}"; do
59+    hooks_dir="$repo/hooks"
60+    existing="$hooks_dir/post-receive"
61+    mkdir -p "$hooks_dir"
62+
63+    if [ -f "$existing" ]; then
64+        if diff -q "$HOOK" "$existing" >/dev/null 2>&1; then
65+            echo "  = $repo (up to date)"
66+            skipped=$((skipped + 1))
67+            continue
68+        fi
69+
70+        if [ "$FORCE" = true ]; then
71+            cp "$HOOK" "$existing"
72+            chmod +x "$existing"
73+            echo "  ⚡ $repo (overwritten)"
74+            overwritten=$((overwritten + 1))
75+            continue
76+        fi
77+
78+        echo "  ✗ $repo (existing hook differs)" >&2
79+        errors=$((errors + 1))
80+        continue
81+    fi
82+
83+    cp "$HOOK" "$existing"
84+    chmod +x "$existing"
85+    echo "  ✓ $repo"
86+    installed=$((installed + 1))
87+done
88+
89+echo ""
90+echo "installed: $installed  skipped: $skipped  overwritten: $overwritten  errors: $errors"
91+
92+if [ "$errors" -gt 0 ]; then
93+    exit 1
94+fi