#!/usr/bin/env bash set -Eeuo pipefail umask 077 APP_NAME="nextcloud-borg-backup" ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" BIN_DIR="${HOME}/.local/bin" CONFIG_DIR="${XDG_CONFIG_HOME:-${HOME}/.config}/${APP_NAME}" STATE_DIR="${XDG_STATE_HOME:-${HOME}/.local/state}/${APP_NAME}" SYSTEMD_USER_DIR="${XDG_CONFIG_HOME:-${HOME}/.config}/systemd/user" for required in python3 borg rclone findmnt flock timeout systemctl; do command -v "$required" >/dev/null || { printf 'Missing dependency: %s\n' "$required" >&2; exit 1; } done [[ "$(borg --version)" == 'borg 1.'* ]] || { printf 'Borg 1.x required\n' >&2; exit 1; } EXISTING_CONFIG=false [[ ! -f "$CONFIG_DIR/config" ]] || EXISTING_CONFIG=true mkdir -p "${HOME}/.local/libexec" "$BIN_DIR" "$CONFIG_DIR" "$STATE_DIR/logs" "$SYSTEMD_USER_DIR" if [[ ! -f "$CONFIG_DIR/config" ]]; then install -m 0600 "$ROOT_DIR/etc/config.example" "$CONFIG_DIR/config" printf 'Installed config: %s\n' "$CONFIG_DIR/config" else printf 'Config exists, not overwriting: %s\n' "$CONFIG_DIR/config" fi if [[ ! -f "$CONFIG_DIR/excludes" ]]; then install -m 0600 "$ROOT_DIR/etc/excludes.example" "$CONFIG_DIR/excludes" fi # Respect installations that store the original key at a custom path. KEY_PATH="$(source "$CONFIG_DIR/config"; printf '%s' "${BORG_PASSPHRASE_FILE:-$CONFIG_DIR/passphrase}")" if [[ ! -f "$KEY_PATH" ]]; then if [[ "$EXISTING_CONFIG" == true ]]; then printf 'Existing installation but missing default passphrase. Restore the key or check the configured key path before updating.\n' >&2 exit 1 fi # Never replace a missing key for an existing encrypted repository with random data. if [[ -f "$CONFIG_DIR/config" ]]; then source "$CONFIG_DIR/config" if timeout 30 test -f "${REPO_DIR:-/net/thor/volume1/NetBackup/Nextcloud/borg-repo}/config"; then printf 'Existing repository but missing passphrase; restore the original key first.\n' >&2 exit 1 fi fi if command -v openssl >/dev/null 2>&1; then openssl rand -base64 48 > "$KEY_PATH" else od -An -tx1 -N48 /dev/urandom | tr -d ' \n' > "$KEY_PATH" printf '\n' >> "$KEY_PATH" fi chmod 0600 "$KEY_PATH" printf 'Created Borg passphrase file: %s\n' "$KEY_PATH" printf 'Store a copy of this passphrase somewhere safe for disaster recovery.\n' fi # Prevent a pending timer event from starting between replacing wrapper and runner. if systemctl --user cat nextcloud-borg-backup.service >/dev/null 2>&1; then systemctl --user stop nextcloud-borg-backup.service nextcloud-borg-backup.timer fi atomic_install() { install -m "$3" "$1" "$2.new" mv -f "$2.new" "$2" } atomic_install "$ROOT_DIR/scripts/nextcloud-borg-runner.py" "${HOME}/.local/libexec/nextcloud-borg-runner.py" 0750 atomic_install "$ROOT_DIR/scripts/nextcloud-borg-backup" "$BIN_DIR/nextcloud-borg-backup" 0750 for unit in nextcloud-borg-backup.service nextcloud-borg-backup.timer nextcloud-borg-health.service nextcloud-borg-health.timer; do atomic_install "$ROOT_DIR/systemd/user/$unit" "$SYSTEMD_USER_DIR/$unit" 0644 done systemctl --user daemon-reload systemctl --user enable nextcloud-borg-backup.timer nextcloud-borg-health.timer systemctl --user restart nextcloud-borg-backup.timer nextcloud-borg-health.timer printf '\nInstalled %s.\n' "$APP_NAME" printf 'Next steps:\n' printf ' 1. Check config: %s\n' "$CONFIG_DIR/config" printf ' 2. Run first backup: systemctl --user start nextcloud-borg-backup.service\n' printf ' 3. Watch logs: journalctl --user -u nextcloud-borg-backup.service -f\n'