blob: 6f70559b0554c0ac9d78e053f515789e75ac7020 [file] [log] [blame]
Euan Kempaabca2e2025-07-21 05:44:44 +00001#!/usr/bin/env bash
Philip Zeyliger254c49f2025-07-17 17:26:24 -07002# Pre-receive hook for sketch git http server
3# Handles refs/remotes/origin/Y pushes by forwarding them to origin/Y
4
5set -e
6
7# Timeout function for commands (macOS compatible)
8# Usage: run_with_timeout <timeout_seconds> <command> [args...]
9#
10# This is here because OX X doesn't ship /usr/bin/timeout by default!?!?
11run_with_timeout() {
12 local timeout=$1
13 shift
14
15 # Run command in background and capture PID
16 "$@" &
17 local cmd_pid=$!
18
19 # Start timeout killer in background
20 (
21 sleep "$timeout"
22 if kill -0 "$cmd_pid" 2>/dev/null; then
23 echo "Command timed out after ${timeout}s, killing process" >&2
24 kill -TERM "$cmd_pid" 2>/dev/null || true
25 sleep 2
26 kill -KILL "$cmd_pid" 2>/dev/null || true
27 fi
28 ) &
29 local killer_pid=$!
30
31 # Wait for command to complete
32 local exit_code=0
33 if wait "$cmd_pid" 2>/dev/null; then
34 exit_code=$?
35 else
36 exit_code=124 # timeout exit code
37 fi
38
39 # Clean up timeout killer
40 kill "$killer_pid" 2>/dev/null || true
41 wait "$killer_pid" 2>/dev/null || true
42
43 return $exit_code
44}
45
46# Read stdin for ref updates
47while read oldrev newrev refname; do
48 # Check if this is a push to refs/remotes/origin/Y pattern
49 if [[ "$refname" =~ ^refs/remotes/origin/(.+)$ ]]; then
50 branch_name="${BASH_REMATCH[1]}"
51
52 # Check if this is a force push by seeing if oldrev is not ancestor of newrev
53 if [ "$oldrev" != "0000000000000000000000000000000000000000" ]; then
54 # Check if this is a fast-forward (oldrev is ancestor of newrev)
55 if ! git merge-base --is-ancestor "$oldrev" "$newrev" 2>/dev/null; then
56 echo "Error: Force push detected to refs/remotes/origin/$branch_name" >&2
57 echo "Force pushes are not allowed" >&2
58 exit 1
59 fi
60 fi
61
62 echo "Detected push to refs/remotes/origin/$branch_name" >&2
63
64 # Verify HTTP_USER_AGENT is set to sketch-intentional-push for forwarding
65 if [ "$HTTP_USER_AGENT" != "sketch-intentional-push" ]; then
66 echo "Error: Unauthorized push to refs/remotes/origin/$branch_name" >&2
67 exit 1
68 fi
69
70 echo "Authorization verified, forwarding to origin" >&2
71
72 # Push to origin using the new commit with 10 second timeout
73 # There's an innocous "ref updates forbidden inside quarantine environment" warning that we can ignore.
74 if ! run_with_timeout 10 git push origin "$newrev:refs/heads/$branch_name"; then
75 echo "Error: Failed to push $newrev to origin/$branch_name (may have timed out)" >&2
76 exit 1
77 fi
78
79 echo "Successfully pushed to origin/$branch_name" >&2
80 fi
81done
82
83exit 0