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>
68 lines
2.3 KiB
Bash
Executable file
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
|