#!/bin/sh # sigabrt-run -- run a command and report how it went. # # sigabrt-run [args...] # sigabrt-run [args...] # # Pulses the start, runs the command, and then sends a heartbeat if it # succeeded or a failure if it did not. What the command printed and the # status it exited with pass through untouched. # # Install it once, rather than fetching it on every run: # # sudo curl -fsS https://sigabrt.dev/run.sh -o /usr/local/bin/sigabrt-run # sudo chmod +x /usr/local/bin/sigabrt-run # # Cron brings a PATH of its own, commonly /sbin:/bin:/usr/sbin:/usr/bin, # which has no /usr/local/bin in it. Put # # PATH=/usr/local/bin:/usr/bin:/bin # # at the top of the crontab, or name this by its full path on the job. # # The endpoint can come from the environment instead of the command line, # which is what a crontab of jobs on one endpoint wants: # # SIGABRT_TOKEN= sigabrt-run ./backup.sh # # SIGABRT_URL takes a whole pulse URL in its place, and SIGABRT_BASE points # the token form at a different instance than the one this came from. # # Nothing here is required. It sends the same three pulses you would send # with curl by hand; see https://sigabrt.dev/docs. set -u DEFAULT_BASE="https://sigabrt.dev" usage() { echo "usage: sigabrt-run [args...]" >&2 exit "${1:-64}" } case ${1:-} in -h | --help) usage 0 ;; esac # Named in the environment, the first argument is the command rather than # the endpoint. target=${SIGABRT_URL:-${SIGABRT_TOKEN:-}} if [ -z "$target" ]; then target=${1:-} [ $# -gt 0 ] && shift fi [ -n "$target" ] || usage [ $# -gt 0 ] || usage base=${SIGABRT_BASE:-$DEFAULT_BASE} case $target in http://* | https://*) pulse_url=$target ;; *) pulse_url="${base%/}/pulse/$target" ;; esac # What the dashboard shows is the heartbeat URL, so take it with any of the # three verbs on the end -- or none -- and speak to all three. pulse_url=${pulse_url%/} pulse_url=${pulse_url%/beat} pulse_url=${pulse_url%/start} pulse_url=${pulse_url%/fail} if command -v curl > /dev/null 2>&1; then send() { curl -fsS -m 10 --retry 3 -o /dev/null "$pulse_url/$1"; } elif command -v wget > /dev/null 2>&1; then send() { wget -q -T 10 -t 3 -O /dev/null "$pulse_url/$1"; } else echo "sigabrt-run: no curl or wget; running the job without pulses" >&2 send() { :; } fi # Monitoring must not be able to change what the job did. Every failure to # reach sigabrt is swallowed, and the status this exits with is the # command's own. pulse() { send "$1" || true; } pulse start "$@" status=$? if [ "$status" -eq 0 ]; then pulse beat else pulse fail fi exit "$status"