ananke/scripts/ananke-self-update.sh

116 lines
3.7 KiB
Bash

#!/usr/bin/env bash
set -euo pipefail
if [[ "${EUID}" -ne 0 ]]; then
echo "ananke-self-update.sh must run as root" >&2
exit 1
fi
REPO_URL="${ANANKE_REPO_URL:-ssh://git@scm.bstein.dev:2242/bstein/ananke.git}"
BRANCH="${ANANKE_REPO_BRANCH:-main}"
REPO_DIR="${ANANKE_REPO_DIR:-/opt/ananke}"
HOST_SHORT="$(hostname -s 2>/dev/null || hostname)"
LOG_FILE="${ANANKE_UPDATE_LOG_FILE:-/var/log/ananke/update.log}"
STATE_FILE="${ANANKE_UPDATE_STATE_FILE:-/var/lib/ananke/update-last.env}"
LOCK_FILE="${ANANKE_UPDATE_LOCK_FILE:-/var/lock/ananke-update.lock}"
ALLOW_QUALITY_FALLBACK="${ANANKE_UPDATE_ALLOW_QUALITY_FALLBACK:-1}"
QUALITY_GATE_MODE="${ANANKE_ENFORCE_QUALITY_GATE:-1}"
mkdir -p "$(dirname "${LOG_FILE}")" "$(dirname "${STATE_FILE}")" "$(dirname "${LOCK_FILE}")"
touch "${LOG_FILE}" "${STATE_FILE}"
chmod 0644 "${LOG_FILE}" "${STATE_FILE}" || true
exec 9>"${LOCK_FILE}"
if ! flock -n 9; then
printf '[self-update] %s another update is already running; exiting\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)" | tee -a "${LOG_FILE}"
exit 0
fi
exec > >(tee -a "${LOG_FILE}") 2>&1
write_state() {
local status="$1"
local detail="$2"
local from_rev="$3"
local to_rev="$4"
local now
now="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
cat > "${STATE_FILE}" <<EOF
timestamp=${now}
host=${HOST_SHORT}
status=${status}
detail=${detail}
from_rev=${from_rev}
to_rev=${to_rev}
branch=${BRANCH}
repo=${REPO_URL}
EOF
chmod 0644 "${STATE_FILE}" || true
}
on_error() {
local line="$1"
local cmd="$2"
local code="$3"
local from_rev="${4:-unknown}"
local to_rev="${5:-unknown}"
echo "[self-update] failure rc=${code} line=${line} cmd=${cmd}"
write_state "failed" "rc=${code};line=${line};cmd=${cmd}" "${from_rev}" "${to_rev}"
exit "${code}"
}
CURRENT_FROM_REV="unknown"
TARGET_TO_REV="unknown"
trap 'on_error "${LINENO}" "${BASH_COMMAND}" "$?" "${CURRENT_FROM_REV}" "${TARGET_TO_REV}"' ERR
echo "[self-update] started host=${HOST_SHORT} branch=${BRANCH} repo=${REPO_URL}"
mkdir -p "$(dirname "${REPO_DIR}")"
if [[ ! -d "${REPO_DIR}/.git" ]]; then
echo "[self-update] cloning ${REPO_URL} into ${REPO_DIR}"
git clone "${REPO_URL}" "${REPO_DIR}"
fi
cd "${REPO_DIR}"
git config --global --add safe.directory "${REPO_DIR}" || true
CURRENT_FROM_REV="$(git rev-parse --short HEAD 2>/dev/null || echo unknown)"
echo "[self-update] syncing ${BRANCH}"
git fetch origin --prune
git checkout "${BRANCH}"
TARGET_TO_REV="$(git rev-parse --short "origin/${BRANCH}" 2>/dev/null || echo unknown)"
git reset --hard "origin/${BRANCH}"
if [[ "${CURRENT_FROM_REV}" == "${TARGET_TO_REV}" ]]; then
echo "[self-update] already up to date at ${CURRENT_FROM_REV}"
else
echo "[self-update] update candidate ${CURRENT_FROM_REV} -> ${TARGET_TO_REV}"
fi
echo "[self-update] running installer"
# Keep host configs aligned with tracked templates so startup/shutdown drills
# always use the latest checklist and safety logic.
if [[ -z "${ANANKE_FORCE_CONFIG_TEMPLATE:-}" ]]; then
case "${HOST_SHORT}" in
titan-db)
export ANANKE_FORCE_CONFIG_TEMPLATE="coordinator"
;;
titan-24)
export ANANKE_FORCE_CONFIG_TEMPLATE="peer"
;;
esac
fi
export ANANKE_ENFORCE_QUALITY_GATE="${QUALITY_GATE_MODE}"
if "${REPO_DIR}/scripts/install.sh"; then
write_state "ok" "install-success" "${CURRENT_FROM_REV}" "${TARGET_TO_REV}"
echo "[self-update] completed successfully"
exit 0
fi
if [[ "${ALLOW_QUALITY_FALLBACK}" == "1" || "${ALLOW_QUALITY_FALLBACK}" == "true" ]]; then
echo "[self-update] install failed in strict mode; retrying once with ANANKE_ENFORCE_QUALITY_GATE=0"
export ANANKE_ENFORCE_QUALITY_GATE=0
"${REPO_DIR}/scripts/install.sh"
write_state "degraded-ok" "fallback-no-quality-gate" "${CURRENT_FROM_REV}" "${TARGET_TO_REV}"
echo "[self-update] completed via fallback mode"
exit 0
fi