#!/usr/bin/env sh set -eu image="${1:?Usage: restore.sh IMAGE VOLUME BACKUP_FILE}" volume="${2:?VOLUME required}" backup="${3:?BACKUP_FILE required}" [ -f "$backup" ] || { echo 'Backup file missing' >&2; exit 1; } backup="$(cd "$(dirname "$backup")" && pwd)/$(basename "$backup")" # Named volumes only. All users must remain stopped for the whole operation. docker volume inspect "$volume" >/dev/null active="$(docker ps -q --filter "volume=$volume")" [ -z "$active" ] || { echo 'Stop every container using the volume before restore' >&2; exit 1; } helper="evofw-restore-$(date +%s)-$$" cleanup() { result=$? trap - EXIT HUP INT TERM # -v removes anonymous helper volumes only, never the target named volume. docker rm -fv "$helper" >/dev/null 2>&1 || true exit "$result" } trap cleanup EXIT trap 'exit 129' HUP trap 'exit 130' INT trap 'exit 143' TERM # Transfer input through the Docker API, including when its daemon is remote. docker create --name "$helper" --network none --cap-drop ALL \ --security-opt no-new-privileges:true --mount "type=volume,src=$volume,dst=/data" \ --entrypoint node "$image" tools/sqlite-backup.mjs restore /tmp/evofw-restore-input.db /data/app.db --offline >/dev/null docker cp "$backup" "$helper:/tmp/evofw-restore-input.db" active="$(docker ps -q --filter "volume=$volume")" [ -z "$active" ] || { echo 'Target volume became active before restore; helper was not started' >&2; exit 1; } docker start --attach "$helper" result="$(docker wait "$helper")" case "$result" in ''|*[!0-9]*) echo 'Invalid restore container exit status' >&2; exit 1;; esac [ "$result" -le 255 ] || { echo 'Invalid restore container exit status' >&2; exit 1; } exit "$result"