68 lines
3.2 KiB
Bash
Executable File
68 lines
3.2 KiB
Bash
Executable File
#!/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)"
|
||
|
||
# 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() {
|
||
mkdir -p "$REPO_DIR/run" 2>/dev/null || true
|
||
printf '{"stage":"%s","at":%s}\n' "$1" "$(date +%s 2>/dev/null || echo 0)" > "$REPO_DIR/run/update-phase.json" 2>/dev/null || true
|
||
chmod 644 "$REPO_DIR/run/update-phase.json" 2>/dev/null || true
|
||
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 1–3 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) ..."
|
||
git pull --ff-only \
|
||
|| die "git pull fehlgeschlagen (lokale Aenderungen oder divergierter Branch). Mit 'git status' 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
|
||
|
||
# 4. apply via the idempotent installer, non-interactively (no prompts; config preserved)
|
||
info "Wende Update an (install.sh) ..."
|
||
exec ./install.sh </dev/null
|