Add self-contained automated server upgrade package

This commit is contained in:
admin committed 2026-09-10 14:23:31 +02:00
1 parent 4d58fc7b66
commit 8e6c9a058f
14 files changed
+908 -26

No files matched your search

+29 -10
View File
@@ -5,8 +5,17 @@ umask 077
ROOT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)
[[ $EUID == 0 ]] || { echo 'Use sudo ./install.sh [--enable-timer]' >&2; exit 1; }
ENABLE_TIMER=false
case ${1:-} in --enable-timer) ENABLE_TIMER=true ;; '') ;; *) echo 'Usage: sudo ./install.sh [--enable-timer]' >&2; exit 1 ;; esac
(( $# <= 1 )) || exit 1
NO_TIMER=false
SNAPSHOT_DIR=''
while (( $# )); do
case $1 in
--enable-timer) ENABLE_TIMER=true; shift ;;
--no-timer) NO_TIMER=true; shift ;;
--snapshot-dir) SNAPSHOT_DIR=${2:?Missing snapshot directory}; shift 2 ;;
*) echo 'Usage: sudo ./install.sh [--enable-timer | --no-timer] [--snapshot-dir DIR]' >&2; exit 1 ;;
esac
done
[[ $NO_TIMER != true || $ENABLE_TIMER != true ]] || exit 1
# Syntax and packaging errors must be caught BEFORE changing the running installation.
for file in "$ROOT_DIR"/usr-local-sbin/*.sh "$ROOT_DIR/lib/common.sh"; do bash -n "$file"; done
python3 - "$ROOT_DIR/lib/backup_set.py" <<'PY'
@@ -21,22 +30,27 @@ if [[ -f /etc/app-backup/app-backup.conf ]]; then
# shellcheck disable=SC1091
source /etc/app-backup/app-backup.conf
fi
[[ ! -L $LOCKFILE ]] || exit 1
exec 9>"$LOCKFILE"
flock -n 9 || { echo 'Backup/restore is running. Retry after it finishes.' >&2; exit 1; }
# shellcheck source=lib/locking.sh
source "$ROOT_DIR/lib/locking.sh"
acquire_backup_lock
if systemctl is-active --quiet app-backup.service; then
echo 'app-backup.service is active. Wait for completion before updating.' >&2
exit 1
fi
TIMER_ACTIVE=false
if systemctl is-active --quiet app-backup.timer; then TIMER_ACTIVE=true; fi
SNAPSHOT=$(mktemp -d /var/backups/app-backup-update-XXXXXXXX)
if [[ -n $SNAPSHOT_DIR ]]; then
[[ $SNAPSHOT_DIR == /* && ! -e $SNAPSHOT_DIR ]] || { echo 'Snapshot directory must be new and absolute'; exit 1; }
mkdir -m 700 -- "$SNAPSHOT_DIR"
SNAPSHOT=$SNAPSHOT_DIR
else SNAPSHOT=$(mktemp -d /var/backups/app-backup-update-XXXXXXXX); fi
chmod 700 "$SNAPSHOT"
declare -a SOURCES TARGETS MODES
add() { SOURCES+=("$ROOT_DIR/$1"); TARGETS+=("$2"); MODES+=("$3"); }
add usr-local-sbin/app-backup.sh /usr/local/sbin/app-backup.sh 750
add usr-local-sbin/app-restore.sh /usr/local/sbin/app-restore.sh 750
add lib/common.sh /usr/local/lib/app-backup/common.sh 640
add lib/locking.sh /usr/local/lib/app-backup/locking.sh 640
add lib/backup_set.py /usr/local/lib/app-backup/backup_set.py 640
add systemd/app-backup.service /etc/systemd/system/app-backup.service 644
# Preserve existing schedules. The new timer template remains available as .dist.
@@ -50,6 +64,7 @@ else add etc/app-backup/app-backup.conf /etc/app-backup/app-backup.conf 600; fi
for f in "$ROOT_DIR"/etc/app-backup/*.example; do add "etc/app-backup/$(basename "$f")" "/etc/app-backup/$(basename "$f")" 600; done
add README.md /usr/local/share/doc/app-backup/README.md 644
add Dokumentation.md /usr/local/share/doc/app-backup/Dokumentation.md 644
add UPDATE_ANLEITUNG.md /usr/local/share/doc/app-backup/UPDATE_ANLEITUNG.md 644
# Save the exact replaced files plus a rollback inventory; do not dereference links.
: > "$SNAPSHOT/present.txt"; : > "$SNAPSHOT/absent.txt"
for i in "${!TARGETS[@]}"; do
@@ -69,8 +84,9 @@ set -euo pipefail
here=$(cd -- "$(dirname -- "$0")" && pwd)
LOCKFILE=/run/app-backup.lock
if [[ -f /etc/app-backup/app-backup.conf ]]; then source /etc/app-backup/app-backup.conf; fi
exec 9>"$LOCKFILE"
flock -n 9 || { echo 'Backup/restore/update is running'; exit 1; }
# shellcheck disable=SC1091
source "$here/locking.sh"
acquire_backup_lock
if systemctl is-active --quiet app-backup.service; then echo 'Backup service is active'; exit 1; fi
systemctl stop app-backup.timer
while IFS= read -r target; do cp -a --remove-destination -- "$here$target" "$target"; done < "$here/present.txt"
@@ -78,6 +94,8 @@ while IFS= read -r target; do rm -f -- "$target"; done < "$here/absent.txt"
systemctl daemon-reload
echo 'Previous package restored; config and timer state require manual review. Timer remains stopped.'
ROLLBACK
cp -- "$ROOT_DIR/lib/locking.sh" "$SNAPSHOT/locking.sh"
chmod 600 "$SNAPSHOT/locking.sh"
chmod 700 "$SNAPSHOT/rollback.sh"
CHANGING=false
on_exit() {
@@ -104,8 +122,9 @@ install -d -m 700 /var/log/app-backup /var/backups/app-backup
systemctl daemon-reload
# A persistent timer can fire immediately. Release the package lock only after
# all files are installed, but before that timer can start the new backup.
flock -u 9
if [[ $ENABLE_TIMER == true ]]; then systemctl enable --now app-backup.timer
if [[ ${APP_BACKUP_INHERIT_LOCK:-0} != 1 ]]; then flock -u 9; fi
if [[ $NO_TIMER == true ]]; then :
elif [[ $ENABLE_TIMER == true ]]; then systemctl enable --now app-backup.timer
elif [[ $TIMER_ACTIVE == true ]]; then systemctl start app-backup.timer; fi
CHANGING=false
echo "Installed. Rollback package: $SNAPSHOT"