#!/bin/sh
# cronpup-run: wrap any command with CronPup heartbeat signals.
#
#   cronpup-run <ping-url> <command> [args...]
#
# Sends /start, runs the command, then reports its exit code (0 = success, anything else = failure),
# attaching the last ~9 KB of combined output so you can see why it failed. The command's own exit code
# and output are passed through unchanged, so cron's MAILTO and your logs behave as before.
# Network problems talking to CronPup never change the job's result. Ping URLs may carry a query
# string (e.g. .../p/KEY/slug?create=1); it is kept on every signal.
#
# Install:  curl -fsSL https://cronpup.com/static/cronpup-run.sh -o /usr/local/bin/cronpup-run
#           chmod +x /usr/local/bin/cronpup-run
# Crontab:  0 3 * * *  cronpup-run https://cronpup.com/p/UUID /opt/jobs/backup.sh
set -u
if [ $# -lt 2 ]; then
  echo "usage: cronpup-run <ping-url> <command> [args...]" >&2
  exit 64
fi
URL=$1; shift
BASE=${URL%%\?*}          # part before any "?"
QUERY=${URL#"$BASE"}      # "?..." or empty
BASE=${BASE%/}
ping() { curl -fsS -m 10 --retry 3 -o /dev/null "$@" 2>/dev/null || true; }

ping "$BASE/start$QUERY"

LOG=$(mktemp "${TMPDIR:-/tmp}/cronpup.XXXXXX" 2>/dev/null) || LOG=""
RCF=""
[ -n "$LOG" ] && { RCF=$(mktemp "${TMPDIR:-/tmp}/cronpup-rc.XXXXXX" 2>/dev/null) || RCF=""; }

if [ -n "$LOG" ] && [ -n "$RCF" ]; then
  # tee output to the terminal/cron and to the log; keep the command's own exit code
  { "$@" 2>&1; echo $? > "$RCF"; } | tee "$LOG"
  RC=$(cat "$RCF" 2>/dev/null)
  case "$RC" in ''|*[!0-9]*) RC=1 ;; esac
  tail -c 9000 "$LOG" | ping --data-binary @- "$BASE/$RC$QUERY"
else
  # no temp files available: run without capturing output (the exit code is still exact)
  "$@"
  RC=$?
  ping "$BASE/$RC$QUERY"
fi
[ -n "$LOG" ] && rm -f -- "$LOG"
[ -n "$RCF" ] && rm -f -- "$RCF"
exit "$RC"
