conductor

CI task system
git clone git://git.finwo.net/app/conductor
Log | Files | Refs | README | LICENSE

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