#!/bin/bash
################################################################################
# Script Name: version/2.1.16/UPDATE.sh
# Description: Repair an updater that cannot install anything, including itself.
# Company: hitechcloud.org
# Copyright (c) hitechcloud.org
################################################################################
#
# Every native server up to and including 2.1.15 has an update that installs
# no binaries.
#
# The native branch of run_update_immediately was a bare `:`, with a comment
# saying the binaries were installed further down by update_hitechcloud.
# Nothing calls update_hitechcloud in that flow. So an update fetched no
# release, installed no binary, refreshed no CLI, ran the migration script,
# wrote the new version number and reported success - leaving a server whose
# version file says one thing and whose binaries say another, and whose
# `update --check` then says "Up to date" forever.
#
# That is also why it cannot fix itself: the fixed update.sh ships inside the
# release bundle, and unpacking the release bundle is one of the things the
# broken update does not do.
#
# This script is the way out, because version-specific scripts are fetched
# from the mirror and run whether or not anything else in the update worked.
# It replaces /usr/local/hitechcloudcli/update.sh with the one from the
# release bundle, and schedules a second update run - the current one is
# holding the update lock, so it cannot simply re-enter.
#
# On a server that already has the fixed updater this is a no-op: the file it
# would install is the file already there.
################################################################################

set -uo pipefail

GREEN='\033[0;32m'; YELLOW='\033[1;33m'; NC='\033[0m'
ok()   { echo -e "[2.1.16] ${GREEN}✓${NC} $*"; }
warn() { echo -e "[2.1.16] ${YELLOW}!${NC} $*" >&2; }
say()  { echo -e "[2.1.16] $*"; }

[[ $EUID -eq 0 ]] || { echo "[2.1.16] This update must run as root." >&2; exit 1; }

MIRROR="${HITECHCLOUD_MIRROR:-https://mirrors.hitechcloud.org}"
INSTALLED=/usr/local/hitechcloudcli/update.sh

# The marker of a working updater: the call that installs the binaries.
has_the_fix() {
    grep -q 'if ! update_hitechcloud_native "\$version"; then' "$1" 2>/dev/null
}

if [[ -f "$INSTALLED" ]] && has_the_fix "$INSTALLED"; then
    ok "the updater on this server already installs binaries; nothing to repair"
    exit 0
fi

say "this server has an updater that installs nothing - repairing it"

tmp_dir=$(mktemp -d)
trap 'rm -rf "$tmp_dir"' EXIT

# From the release bundle, which is the same file the release ships, rather
# than from a repository this server has no credential for.
if ! curl -4fsSL --retry 3 --max-time 120 "${MIRROR}/releases/latest/runtime.tar.gz" \
        -o "${tmp_dir}/runtime.tar.gz"; then
    warn "could not download the release bundle from ${MIRROR}"
    warn "Repair by hand, then run 'hitechcloudcli update --force':"
    warn "  curl -fsSL ${MIRROR}/releases/latest/runtime.tar.gz | \\"
    warn "    tar -xzO --wildcards '*/hitechcloudcli-scripts/update.sh' > ${INSTALLED}"
    exit 1
fi

if ! tar -xzO -f "${tmp_dir}/runtime.tar.gz" --wildcards \
        '*/hitechcloudpanel/hitechcloudcli-scripts/update.sh' > "${tmp_dir}/update.sh" 2>/dev/null ||
   [[ ! -s "${tmp_dir}/update.sh" ]]; then
    warn "the release bundle carries no hitechcloudcli-scripts/update.sh"
    exit 1
fi

# Checked before it replaces a working file: an update.sh that does not parse
# is a server that can never update again, which is worse than the bug.
if ! bash -n "${tmp_dir}/update.sh" 2>/dev/null; then
    warn "the update.sh in the bundle does not parse; leaving the current one alone"
    exit 1
fi
if ! has_the_fix "${tmp_dir}/update.sh"; then
    warn "the update.sh in the bundle is still the version that installs nothing"
    warn "This release did not carry the fix. Nothing was changed."
    exit 1
fi

cp -a "$INSTALLED" "${INSTALLED}.before-2.1.16" 2>/dev/null || true
install -m 644 "${tmp_dir}/update.sh" "$INSTALLED"
ok "replaced ${INSTALLED} (the previous one is beside it as .before-2.1.16)"

# The binaries still have to be installed, and this script is running inside
# an update that is holding /var/lock/hitechcloud_update.lock. Scheduled just
# after, rather than re-entered.
if command -v systemd-run >/dev/null 2>&1; then
    if systemd-run --quiet --on-active=180 --unit=hitechcloud-repair-update \
         /usr/local/bin/hitechcloudcli update --force >/dev/null 2>&1; then
        ok "a second update run is scheduled in three minutes; it will install the binaries"
        exit 0
    fi
fi

warn "the binaries on this server are still the ones it had before."
warn "Run this once the current update has finished:"
warn "  hitechcloudcli update --force"
