#!/usr/bin/env bash # Deploy an image tag on the Kvitt Pi (SPEC §10.3, §10.10). Run ON the Pi: /opt/kvitt/deploy.sh # 1. backup first (backup-now.sh: Drive upload confirmed, last-success updated), because a tag may carry a # migration and the Pi cannot tell beforehand (the rule "backup before a deploy with a migration" is applied # to every deploy); skipped when no database exists yet, or with --skip-backup (rollback while Drive is down) # 2. sets KVITT_TAG in /opt/kvitt/.env (the previous tag is kept in /opt/kvitt/previous-tag) # 3. docker compose pull && docker compose up -d, waits for server to be healthy, prints GET /health # Rollback after a failed migration: /opt/kvitt/deploy.sh "$(cat /opt/kvitt/previous-tag)"; if a migration had # partially applied, restore the pre-deploy backup (infra/RESTORE.md). set -euo pipefail here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # shellcheck source=lib.sh . "$here/lib.sh" skip_backup=0 tag="" for arg in "$@"; do case "$arg" in --skip-backup) skip_backup=1 ;; -h|--help) sed -n '2,10p' "$0"; exit 0 ;; -*) die "unknown option $arg" ;; *) [[ -z "$tag" ]] || die "usage: $0 [--skip-backup]"; tag=$arg ;; esac done [[ -n "$tag" ]] || die "usage: $0 [--skip-backup]" [[ "$tag" =~ ^[A-Za-z0-9_][A-Za-z0-9_.-]{0,127}$ ]] || die "not a valid image tag: $tag" [[ -f "$KVITT_DIR/.env" ]] || die "$KVITT_DIR/.env is missing: run install.sh first" current=$(env_value KVITT_TAG) say "Deploying $tag (running: ${current:-none})" # 1. Backup before the deploy. if (( skip_backup == 1 )); then say "Backup skipped (--skip-backup)" elif [[ "$(service_health server)" == none ]] || ! compose exec -T backup test -f /data/kvitt.sqlite 2>/dev/null; then say "No running server with a database: no backup needed" else "$here/backup-now.sh" || die "the pre-deploy backup failed; nothing deployed (fix the backup, or use --skip-backup knowingly)" fi # 2. Tag. [[ -n "$current" ]] && printf '%s\n' "$current" > "$KVITT_DIR/previous-tag" env_set KVITT_TAG "$tag" # 3. Pull and start. if ! compose pull; then [[ -n "$current" ]] && env_set KVITT_TAG "$current" die "pull of $tag failed; KVITT_TAG left at ${current:-its old value}" fi compose up -d --remove-orphans if ! wait_healthy server 180; then compose logs --tail 50 server || true die "server is not healthy on $tag. Roll back: $KVITT_DIR/deploy.sh ${current:-} --skip-backup" fi compose ps print_health || die "server is healthy but /health through Caddy failed (docker compose logs caddy cert-sync)" say "Deployed $tag."