clusev/update.sh

85 lines
4.4 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#!/usr/bin/env bash
# Clusev updater — pull the latest code and re-run the idempotent installer (root required).
# One command to update an existing install:
#
# sudo ./update.sh
#
# In order:
# 1. Mark the repo a safe git dir for root. install.sh chowns the tree to the `clusev` user,
# so a root-run `git pull` would otherwise refuse with "dubious ownership".
# 2. git pull --ff-only on the tracked branch. Aborts cleanly on any conflict / divergence —
# it NEVER discards local changes.
# 3. Self-update: if update.sh itself changed in the pull, re-exec the NEW copy once (guarded
# against loops) so improvements to this very script take effect on the same run.
# 4. Hand off to ./install.sh — idempotent: rebuilds the image, restarts the stack, migrates,
# refreshes the MOTD and fixes ownership. Run NON-interactively (stdin from /dev/null) so
# it never re-prompts; secrets and the configured domain / ACME e-mail are preserved.
set -euo pipefail
cd "$(dirname "$0")"
REPO_DIR="$(pwd)"
# Never let a private-repo credential miss HANG the update: git must fail fast instead of blocking on
# an interactive username/password prompt (the systemd updater has no TTY, so a prompt would wait
# forever — the classic "update spins with no error"). Also abort a stalled transfer instead of
# hanging on it. The pull itself is additionally wrapped in `timeout` below.
export GIT_TERMINAL_PROMPT=0
export GIT_HTTP_LOW_SPEED_LIMIT="${GIT_HTTP_LOW_SPEED_LIMIT:-1000}"
export GIT_HTTP_LOW_SPEED_TIME="${GIT_HTTP_LOW_SPEED_TIME:-30}"
# set_stage: publish a coarse update stage for the in-browser progress screen (best-effort — the
# app container serves run/update-phase.json to it). A write failure must never fail the update.
# Written first as "fetch" so the screen leaves the time-guess behind the moment the pull begins,
# and so a stale stage from a previous update is reset.
set_stage() {
# Symlink-safe write: ./run is the container-writable bind mount, so update-phase.json could be
# pre-planted as a symlink; write a temp in ./run and rename(2) over the target so this root write
# replaces the link itself and never follows it (mirror of run_write_json in the restart-sentinel).
local d="$REPO_DIR/run" tmp
mkdir -p "$d" 2>/dev/null || true
tmp="$(mktemp "${d}/.phase.XXXXXX" 2>/dev/null)" || return 0
printf '{"stage":"%s","at":%s}\n' "$1" "$(date +%s 2>/dev/null || echo 0)" > "$tmp" 2>/dev/null || { rm -f "$tmp" 2>/dev/null; return 0; }
chmod 644 "$tmp" 2>/dev/null || true
mv -f "$tmp" "${d}/update-phase.json" 2>/dev/null || rm -f "$tmp" 2>/dev/null
return 0
}
set_stage fetch
bold() { printf '\033[1m%s\033[0m\n' "$*"; }
info() { printf ' %s\n' "$*"; }
die() { printf '\033[31m x %s\033[0m\n' "$*" >&2; exit 1; }
[ "$(id -u)" = 0 ] || die "Bitte mit sudo ausfuehren: sudo ./update.sh"
[ -f docker-compose.prod.yml ] || die "Keine Clusev-Installation hier (docker-compose.prod.yml fehlt)."
[ -x ./install.sh ] || [ -f ./install.sh ] || die "install.sh fehlt — Repo unvollstaendig."
command -v git >/dev/null 2>&1 || die "git ist nicht installiert."
bold "Clusev Update"
echo "============="
# Steps 13 run only on the first pass; a re-exec (after a self-update) jumps straight to step 4.
if [ "${CLUSEV_UPDATE_REEXEC:-0}" != 1 ]; then
# 1. let root operate on the clusev-owned working tree
git config --global --add safe.directory "$REPO_DIR" 2>/dev/null || true
# 2. fast-forward pull only — never auto-merge or throw away local edits
before_hash="$(sha256sum -- "$0" | cut -d' ' -f1)"
info "Hole Aktualisierungen (git pull --ff-only) ..."
timeout 300 git pull --ff-only \
|| die "git pull fehlgeschlagen — Timeout, fehlende Zugangsdaten zum privaten Repo, lokale Aenderungen oder divergierter Branch. Mit 'git status' und dem Repo-Zugriff (Token im origin-Remote) pruefen und erneut versuchen."
after_hash="$(sha256sum -- "$0" | cut -d' ' -f1)"
# 3. self-update: relaunch the new update.sh if it changed
if [ "$before_hash" != "$after_hash" ]; then
info "update.sh wurde aktualisiert — starte die neue Version ..."
exec env CLUSEV_UPDATE_REEXEC=1 "$0" "$@"
fi
fi
# Refresh the update source from the (possibly changed) clone origin before handing off.
[ -x ./scripts/set-repository-url.sh ] && ./scripts/set-repository-url.sh "$(pwd)" ./.env || true
# 4. apply via the idempotent installer, non-interactively (no prompts; config preserved)
info "Wende Update an (install.sh) ..."
exec ./install.sh </dev/null