post-receive (2865B)
1 #!/bin/sh 2 # hooks/post-receive - notify conductors of pushed commits 3 # 4 # Install into a bare repository as hooks/post-receive, chmod +x. 5 # Reads "<old> <new> <ref>" per pushed ref on stdin. Branches and tags are 6 # forwarded; a pipeline decides what to build with an only: refs rule. 7 # 8 # Expects conductor.conf in the parent directory with lines: 9 # <trigger_url> <secret> 10 # Blank lines and lines starting with # are ignored. 11 12 set -eu 13 14 ZERO="0000000000000000000000000000000000000000" 15 16 DIRSELF=$(dirname $(realpath $0)) 17 CONFIG="${DIRSELF}/../conductor.conf" 18 19 log() { printf '[conductor] %s\n' "$*" >&2; } 20 21 if [ ! -f "${CONFIG}" ]; then 22 log "no conductor.conf found; not triggering" 23 exit 0 24 fi 25 26 if command -v curl >/dev/null 2>&1; then 27 http_client=curl 28 elif command -v wget >/dev/null 2>&1; then 29 http_client=wget 30 else 31 log "neither curl nor wget is available; not triggering" 32 exit 0 33 fi 34 35 trigger() { 36 _url="$1" 37 _secret="$2" 38 _payload="$3" 39 _ref="$4" 40 41 if [ -n "${_secret}" ]; then 42 digest=$(printf '%s' "${_payload}" | openssl dgst -sha256 -hmac "${_secret}" | sed 's/^.*[= ]//') 43 signature="sha256=${digest}" 44 else 45 signature="" 46 log "warning: no secret for ${_url}, sending unsigned" 47 fi 48 49 if [ "${http_client}" = curl ]; then 50 response=$(curl -fsS -m 10 -X POST "${_url}" \ 51 -H 'Content-Type: application/json' \ 52 -H "X-Hub-Signature-256: ${signature}" \ 53 -d "${_payload}" 2>&1) || { 54 log "trigger failed for ${_ref}: ${response}" 55 return 56 } 57 else 58 response=$(wget -qO- --timeout=10 \ 59 --header='Content-Type: application/json' \ 60 --header="X-Hub-Signature-256: ${signature}" \ 61 --post-data="${_payload}" "${_url}" 2>&1) || { 62 log "trigger failed for ${_ref}: ${response}" 63 return 64 } 65 fi 66 67 log "${response}" 68 } 69 70 while read -r old new ref; do 71 if [ "${new}" = "${ZERO}" ]; then 72 log "skipping ${ref}, deleted" 73 continue 74 fi 75 76 case "${ref}" in 77 refs/heads/*|refs/tags/*) ;; 78 *) log "skipping ${ref}"; continue ;; 79 esac 80 81 # An annotated tag names a tag object, and the conductor wants the commit. 82 sha=$(git rev-parse --verify --quiet "${new}^{commit}") || { 83 log "skipping ${ref}, not a commit" 84 continue 85 } 86 87 if [ "${old}" = "${ZERO}" ]; then 88 base="" 89 else 90 base=$(git rev-parse --verify --quiet "${old}^{commit}" || true) 91 fi 92 93 payload=$(printf '{"sha":"%s","base":"%s","ref":"%s","actor":"%s"}' \ 94 "${sha}" "${base}" "${ref}" "${USER:-git}") 95 96 log "triggering ${ref} at $(echo "${sha}" | cut -c1-12)" 97 98 while IFS= read -r line; do 99 case "${line}" in 100 ''|\#*) continue ;; 101 esac 102 103 url=$(printf '%s' "${line}" | awk '{print $1}') 104 secret=$(printf '%s' "${line}" | awk '{print $2}') 105 106 [ -z "${url}" ] && continue 107 108 trigger "${url}" "${secret}" "${payload}" "${ref}" "${new}" 109 done < "${CONFIG}" 110 done 111 112 exit 0