printhive/tools/volumes-to-folders.sh
dlawler489 6c10cf6b37 Let the data folders be set per install, database separately
DATA_DIR takes the files and PGDATA_DIR the database, so the database can
stay on a filesystem that supports ownership when the files live on a disk
that doesn't. The example .env points both at /Volumes/Data/printhive.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-20 16:01:43 +10:00

68 lines
2.3 KiB
Bash
Executable file

#!/usr/bin/env bash
# Copies Printhive's data out of Docker's named volumes into ./data, for the
# switch to host folders. Run it on the server, from the folder holding
# docker-compose.yml, with the stack stopped.
#
# It only copies: the named volumes are left exactly as they were, so if
# anything looks wrong you can put the old docker-compose.yml back and start
# again with nothing lost.
set -euo pipefail
# Same settings Compose uses, so the copy lands where the stack will look.
if [ -f .env ]; then
DATA_DIR="${DATA_DIR:-$(grep -E '^DATA_DIR=' .env | tail -1 | cut -d= -f2-)}"
PGDATA_DIR="${PGDATA_DIR:-$(grep -E '^PGDATA_DIR=' .env | tail -1 | cut -d= -f2-)}"
fi
DATA_DIR="${DATA_DIR:-./data}"
PGDATA_DIR="${PGDATA_DIR:-./data/pgdata}"
VOLUMES=(pgdata uploads thumbnails glb extracted)
# Where each volume's contents should end up.
target_for() {
case "$1" in
pgdata) echo "$PGDATA_DIR" ;;
*) echo "$DATA_DIR/$1" ;;
esac
}
# Compose names volumes "<project>_<name>", the project being this folder's name
# unless it's set elsewhere.
PROJECT="${COMPOSE_PROJECT_NAME:-$(basename "$(pwd)")}"
if docker compose ps --status running --quiet 2>/dev/null | grep -q .; then
echo "Printhive is still running. Stop it first: docker compose down" >&2
exit 1
fi
echo "Project: $PROJECT"
echo "Files: $DATA_DIR"
echo "Database: $PGDATA_DIR"
echo
for name in "${VOLUMES[@]}"; do
volume="${PROJECT}_${name}"
target="$(target_for "$name")"
if ! docker volume inspect "$volume" >/dev/null 2>&1; then
echo "· $volume — no such volume, skipping (nothing to copy)"
mkdir -p "$target"
continue
fi
if [ -d "$target" ] && [ -n "$(ls -A "$target" 2>/dev/null)" ]; then
echo "· $target already has files in it — skipping, so nothing is overwritten" >&2
continue
fi
mkdir -p "$target"
# -a keeps ownership and timestamps, which Postgres's data folder needs.
docker run --rm \
-v "$volume":/from:ro \
-v "$(cd "$(dirname "$target")" && pwd)/$(basename "$target")":/to \
alpine sh -c 'cp -a /from/. /to/'
echo "· $volume → $target ($(du -sh "$target" | cut -f1))"
done
echo
echo "Done. Start it up again with: docker compose up -d"
echo "Once you're happy, the old volumes can be removed with:"
for name in "${VOLUMES[@]}"; do echo " docker volume rm ${PROJECT}_${name}"; done