infra/pi: install.sh keeps the deployed KVITT_TAG and checks python3; env_set cleans its temp file; backup.sh removes half-written archives on failure

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
This commit is contained in:
2026-10-18 22:11:07 +02:00
parent fc6126c6af
commit b647faf715
3 changed files with 17 additions and 7 deletions
+3 -1
View File
@@ -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"
+10 -1
View File
@@ -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 <tag>"
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
+4 -5
View File
@@ -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() {