#!/bin/sh
# bob-agent-update — install the newest bob-agent build and restart every
# server instance (#12) and every supervised tool host (#57). Run as root:
#   sudo bob-agent-update
#
# The Forgejo Actions workflow publishes the .deb to the "bob-agent"
# generic package under a *per-commit* version (0.0.0+git<UTC-ts>.<sha>)
# on every push to main. Versions are immutable — the registry 409s on an
# existing file, so the workflow never deletes or replaces a version —
# which means an updater can never race a publish: a version, once
# published, is always there. The newest one is simply the
# lexicographically largest (the timestamp is fixed-width UTC, so byte
# order == chronological order).
#
# This script finds that newest version through the Forgejo API, downloads
# its file, installs it with dpkg, then restarts all enabled bob-agent@<user>
# server instances and all enabled bob-agent-toolhost@<user> tool hosts, so
# every unix user's server and every supervised tool host runs the new
# binary. Restarts are lossless: the server drains (in-flight tool work
# finishes) before exiting. Servers restart FIRST, tool hosts second (#57):
# a host connects to the server, so it must reconnect to the restarted one
# — which it does on its own anyway (a host never exits on connection loss),
# the restart just makes it pick up the new binary too.
#
# Configuration via environment (or /etc/bob-agent/update.conf, sourced
# below): BOB_FORGEJO_HOST, BOB_FORGEJO_REPO, BOB_FORGEJO_TOKEN. The token
# is only needed when the repo/package is private; it must be readable by
# root. Run as root.
set -eu

if [ "$(id -u)" -ne 0 ]; then
    echo "bob-agent-update: run me as root (sudo bob-agent-update)" >&2
    exit 1
fi

if [ -f /etc/bob-agent/update.conf ]; then
    # shellcheck disable=SC1091
    . /etc/bob-agent/update.conf
fi

HOST="${BOB_FORGEJO_HOST:-https://git.blahonga.org}"
REPO="${BOB_FORGEJO_REPO:-art/bob-agent}"
PKG="bob-agent"
OWNER="${REPO%%/*}"
API="$HOST/api/v1/packages"

# Wrap curl: add the token when configured (private package).
fetch() {
    if [ -n "${BOB_FORGEJO_TOKEN:-}" ]; then
        curl -fsSL -H "Authorization: token $BOB_FORGEJO_TOKEN" "$@"
    else
        curl -fsSL "$@"
    fi
}

# Newest published version. Versions are 0.0.0+git<UTC-ts>.<sha>, so byte
# order == chronological order; page through the API until exhausted. Only
# versions of that shape count — the pre-per-commit "latest" placeholder
# would otherwise win the sort (l > 0 in ASCII) and pin machines to a
# frozen build forever.
versions=""
page=1
while :; do
    batch="$(fetch "$API/$OWNER?type=generic&q=$PKG&page=$page&limit=100" \
        | grep -oE '"version"[[:space:]]*:[[:space:]]*"[^"]*"' \
        | sed 's/^"version"[[:space:]]*:[[:space:]]*"//; s/"$//' \
        | grep -E '^0\.0\.0\+git[0-9]{14}\.[0-9a-f]+$')"
    [ -n "$batch" ] || break
    versions="$versions
$batch"
    count="$(printf '%s\n' "$batch" | wc -l)"
    [ "$count" -lt 100 ] && break
    page=$((page + 1))
done

version="$(printf '%s\n' "$versions" | LC_ALL=C sort | tail -1)"
[ -n "$version" ] || {
    echo "bob-agent-update: no per-commit versions of package '$PKG' found at $API/$OWNER" >&2
    exit 1
}

# The version's file: take the first file id listed (each version holds
# exactly one file — the .deb).
fileid="$(fetch "$API/$OWNER/generic/$PKG/$version/files" \
    | grep -oE '"id"[[:space:]]*:[[:space:]]*[0-9]+' | head -1 \
    | sed 's/^"id"[[:space:]]*:[[:space:]]*//')"
[ -n "$fileid" ] || {
    echo "bob-agent-update: no file in version '$version' of package '$PKG'" >&2
    exit 1
}

url="$HOST/$OWNER/-/packages/generic/$PKG/$version/files/$fileid"
tmp="$(mktemp -d)"
trap 'rm -rf "$tmp"' EXIT
deb="$tmp/bob-agent-latest.deb"

echo "Fetching $url"
fetch -o "$deb" "$url"

echo "Installing $deb"
dpkg -i "$deb"

echo "Restarting all bob-agent server instances"
systemctl restart 'bob-agent@*.service'

echo "Restarting all supervised tool hosts"
systemctl restart 'bob-agent-toolhost@*.service'

echo "Done. Servers and tool hosts were restarted (servers first, hosts"
echo "second, so hosts reconnect to the restarted server). Tool hosts are"
echo "supervised per machine with: systemctl enable --now bob-agent-toolhost@<user>"
echo "Sessions are server-side, so reattaching keeps the same conversation."
