#!/usr/bin/env bash
# Deploy a build of Steid to a running instance.
#
#     ./deploy.sh                     # build, upload, upgrade, verify
#     ./deploy.sh --skip-build        # use whatever is already in dist/
#     ./deploy.sh --host steid --domain jpgill.dev
#
# This is the operator's script, not the stranger's — `install.sh` is what someone
# else runs to install Steid for the first time. This one assumes an instance
# already exists and you want it running a newer build.
#
# WHY IT DOES MORE THAN scp-AND-RUN. An upgrade replaces the binary of a live
# service. Three things can go wrong and each is silent: the new build might not
# boot, the state might not survive, and a mistake leaves nothing to go back to.
# So it snapshots the database and the current binary first, upgrades, then
# *checks the site actually works* — and restores if it does not.

set -euo pipefail

HOST="steid"                  # an ssh alias or user@address
DOMAIN="jpgill.dev"
TARGET="x86_64-unknown-linux-gnu"
SKIP_BUILD=0
VERSION=""

die() { echo "deploy.sh: $*" >&2; exit 1; }
say() { echo "==> $*"; }

while [ $# -gt 0 ]; do
    case "$1" in
        --host)       HOST="${2:?--host needs a value}"; shift 2 ;;
        --domain)     DOMAIN="${2:?--domain needs a value}"; shift 2 ;;
        --target)     TARGET="${2:?--target needs a triple}"; shift 2 ;;
        --version)    VERSION="${2:?--version needs a value}"; shift 2 ;;
        --skip-build) SKIP_BUILD=1; shift ;;
        -h|--help)    sed -n '2,17p' "$0" | sed 's/^#\{1,2\} \{0,1\}//'; exit 0 ;;
        *) die "unknown argument: $1" ;;
    esac
done

cd "$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd)"

[ -n "$VERSION" ] || VERSION="$(grep -m1 '^version *= *"' Cargo.toml | sed 's/.*"\(.*\)".*/\1/')"
NAME="steid-${VERSION}-${TARGET}"
TARBALL="dist/${NAME}.tar.gz"

# Refuse to deploy a working tree that is not what is committed. The instance
# should always be running a commit you can point at, or debugging it later means
# guessing what was on the machine that built it.
if [ -n "$(git status --porcelain)" ]; then
    die "working tree is dirty — commit first, so the deployed build has a commit to name"
fi
COMMIT="$(git rev-parse --short HEAD)"

if [ "$SKIP_BUILD" = 0 ]; then
    say "building ${NAME} from ${COMMIT}"
    ./release.sh --target "$TARGET" --version "$VERSION" >/dev/null
else
    say "skipping build (--skip-build)"
fi
[ -f "$TARBALL" ] || die "no ${TARBALL} — build it, or drop --skip-build"

say "checking the instance is reachable"
ssh -o BatchMode=yes "$HOST" true || die "cannot ssh to ${HOST}"

# --- snapshot, so there is something to go back to --------------------------

STAMP="$(date +%Y%m%d-%H%M%S)"
say "snapshotting state and the current binary (${STAMP})"
ssh -o BatchMode=yes "$HOST" "
    set -e
    sudo mkdir -p /var/backups/steid
    sudo tar czf /var/backups/steid/state-${STAMP}.tar.gz -C /var/lib steid
    sudo tar czf /var/backups/steid/binary-${STAMP}.tar.gz -C /opt steid
    sudo find /var/backups/steid -name '*.tar.gz' -mtime +14 -delete
"

# --- upgrade ----------------------------------------------------------------

say "uploading"
scp -q -o BatchMode=yes "$TARBALL" "${TARBALL}.sha256" install.sh "${HOST}:"

say "upgrading"
ssh -o BatchMode=yes "$HOST" "sudo sh ./install.sh --domain '${DOMAIN}' --tarball './${NAME}.tar.gz'" \
    | grep -E '^==>|is running' || true

# --- verify, and roll back if it is not actually serving --------------------

say "verifying https://${DOMAIN}"
OK=0
for _ in $(seq 1 15); do
    sleep 2
    if [ "$(curl -s -o /dev/null -m 10 -w '%{http_code}' "https://${DOMAIN}/healthz")" = "200" ]; then
        OK=1; break
    fi
done

if [ "$OK" = 1 ]; then
    say "healthy — ${VERSION} (${COMMIT}) is live at https://${DOMAIN}"
    ssh -o BatchMode=yes "$HOST" "systemctl is-active steid caddy | paste -sd' ' -" | sed 's/^/    steid caddy: /'
else
    echo "deploy.sh: the new build is NOT serving. Rolling back." >&2
    ssh -o BatchMode=yes "$HOST" "
        set -e
        sudo systemctl stop steid
        sudo rm -rf /opt/steid
        sudo tar xzf /var/backups/steid/binary-${STAMP}.tar.gz -C /opt
        sudo systemctl start steid
    "
    sleep 4
    ROLLED="$(curl -s -o /dev/null -m 10 -w '%{http_code}' "https://${DOMAIN}/healthz")"
    echo "deploy.sh: rolled back to the previous binary (healthz ${ROLLED})." >&2
    echo "deploy.sh: state was NOT restored — the database is untouched by an upgrade," >&2
    echo "  and /var/backups/steid/state-${STAMP}.tar.gz holds it if you need it." >&2
    exit 1
fi
