diff --git a/infra/backup/backup.sh b/infra/backup/backup.sh index c7f70b6..51be77d 100755 --- a/infra/backup/backup.sh +++ b/infra/backup/backup.sh @@ -16,8 +16,10 @@ BACKUP_DIR=${BACKUP_DIR:-/backups} DB="$DATA_DIR/kvitt.sqlite" STAGING="$BACKUP_DIR/staging" +db_out=""; pics_out="" log() { echo "$(date -u +%Y-%m-%dT%H:%M:%SZ) backup: $*"; } -fail() { log "FAILED: $*"; rm -f "$STAGING/kvitt.sqlite"; exit 1; } +# Removes the snapshot and any half-written archive of this run; finished archives stay for the next upload. +fail() { log "FAILED: $*"; rm -f "$STAGING/kvitt.sqlite" ${db_out:+"$db_out.tmp"} ${pics_out:+"$pics_out.tmp"}; exit 1; } [ -f "$DB" ] || fail "no database at $DB" mkdir -p "$STAGING" diff --git a/infra/pi/install.sh b/infra/pi/install.sh index 45f7b3b..334e728 100755 --- a/infra/pi/install.sh +++ b/infra/pi/install.sh @@ -6,7 +6,8 @@ # 2. checks .env (no CHANGE_ME left in the keys the stack reads) # 3. creates /opt/kvitt/{secrets,import,apk} and installs the secret files from ./secrets/ with their modes # 4. trusts the plain-HTTP registry in /etc/docker/daemon.json (insecure-registries) and logs in when credentials are set -# 5. copies compose.yaml, Caddyfile, .env and the operation scripts to /opt/kvitt +# 5. copies compose.yaml, Caddyfile, .env and the operation scripts to /opt/kvitt; a KVITT_TAG that deploy.sh set +# in /opt/kvitt/.env is kept, so re-running an older bundle never rolls the server back to the bundle's tag # 6. pulls the images, gives the named volumes their owners, starts the stack, waits for /health # Uses sudo for /opt/kvitt, the secret files and the Docker daemon configuration. set -euo pipefail @@ -19,6 +20,7 @@ command -v docker >/dev/null || die "docker is not installed (README step 2)" docker compose version >/dev/null 2>&1 || die "the docker compose plugin is missing (README step 2)" docker info >/dev/null 2>&1 || die "cannot talk to Docker as $(id -un): add yourself to the docker group (sudo usermod -aG docker $(id -un)), log out and in" command -v curl >/dev/null || die "curl is missing: sudo apt-get install -y curl" +command -v python3 >/dev/null || die "python3 is missing (step 4 edits daemon.json with it): sudo apt-get install -y python3" # 2. .env env_src="$bundle/.env" @@ -105,9 +107,16 @@ else fi # 5. Stack files and operation scripts. +installed_tag="" +[[ -f "$KVITT_DIR/.env" ]] && installed_tag=$(env_value KVITT_TAG) +bundle_tag=$(env_value KVITT_TAG "$env_src") install -m 0644 "$bundle/compose.yaml" "$KVITT_DIR/compose.yaml" install -m 0644 "$bundle/Caddyfile" "$KVITT_DIR/Caddyfile" install -m 0600 "$env_src" "$KVITT_DIR/.env" +if [[ -n "$installed_tag" && "$installed_tag" != "$bundle_tag" ]]; then + env_set KVITT_TAG "$installed_tag" + say "KVITT_TAG stays at $installed_tag (set by deploy.sh; the bundle's .env says $bundle_tag). Other tag: $KVITT_DIR/deploy.sh " +fi for script in lib.sh deploy.sh bootstrap.sh backup-now.sh install-apk.sh; do install -m 0755 "$bundle/$script" "$KVITT_DIR/$script" done diff --git a/infra/pi/lib.sh b/infra/pi/lib.sh index dc5705e..12e116c 100755 --- a/infra/pi/lib.sh +++ b/infra/pi/lib.sh @@ -21,13 +21,12 @@ env_set() { local key=$1 value=$2 file=${3:-$KVITT_DIR/.env} tmp tmp=$(mktemp "$file.XXXXXX") if grep -q "^$key=" "$file"; then - awk -v k="$key" -v v="$value" 'index($0, k "=") == 1 { print k "=" v; next } { print }' "$file" > "$tmp" + awk -v k="$key" -v v="$value" 'index($0, k "=") == 1 { print k "=" v; next } { print }' "$file" > "$tmp" \ + || { rm -f "$tmp"; return 1; } else - cat "$file" > "$tmp" - printf '%s=%s\n' "$key" "$value" >> "$tmp" + { cat "$file" && printf '%s=%s\n' "$key" "$value"; } > "$tmp" || { rm -f "$tmp"; return 1; } fi - chmod --reference="$file" "$tmp" - mv -f "$tmp" "$file" + if ! { chmod --reference="$file" "$tmp" && mv -f "$tmp" "$file"; }; then rm -f "$tmp"; return 1; fi } compose() {