feat: add flyway cli wrapper and staging restore
This commit is contained in:
137
recreate-staging.sh
Executable file
137
recreate-staging.sh
Executable file
@@ -0,0 +1,137 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
CONTAINER_NAME=${STAGING_MYSQL_CONTAINER:-peipei-mysql-staging}
|
||||
VOLUME_NAME=${STAGING_MYSQL_VOLUME:-peipei-mysql-staging-data}
|
||||
HOST_PORT=${STAGING_MYSQL_PORT:-3307}
|
||||
DB_NAME=${STAGING_MYSQL_DB:-play-with}
|
||||
ROOT_PASSWORD=${STAGING_MYSQL_ROOT_PASSWORD:-root}
|
||||
MYSQL_IMAGE=${STAGING_MYSQL_IMAGE:-mysql:8.0.32}
|
||||
|
||||
usage() {
|
||||
cat <<USAGE
|
||||
Usage: $0 <path-to-backup.sql[.gz|.xz]>
|
||||
|
||||
Recreates the local staging MySQL container (${CONTAINER_NAME}) and imports the given SQL dump.
|
||||
Environment overrides: STAGING_MYSQL_CONTAINER, STAGING_MYSQL_VOLUME, STAGING_MYSQL_PORT,
|
||||
STAGING_MYSQL_DB, STAGING_MYSQL_ROOT_PASSWORD, STAGING_MYSQL_IMAGE.
|
||||
USAGE
|
||||
exit 1
|
||||
}
|
||||
|
||||
if [[ $# -ne 1 ]]; then
|
||||
usage
|
||||
fi
|
||||
|
||||
backup_path=$1
|
||||
if [[ ! -f "$backup_path" ]]; then
|
||||
echo "[ERROR] Backup file not found: $backup_path" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v docker >/dev/null 2>&1; then
|
||||
echo "[ERROR] docker command not found" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
port_in_use() {
|
||||
if command -v lsof >/dev/null 2>&1; then
|
||||
lsof -iTCP:"$HOST_PORT" -sTCP:LISTEN -Pn >/dev/null 2>&1 && return 0
|
||||
fi
|
||||
if command -v netstat >/dev/null 2>&1; then
|
||||
netstat -an | grep -E "\.$HOST_PORT .*LISTEN" >/dev/null 2>&1 && return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
confirm_remove() {
|
||||
local prompt="$1"
|
||||
read -r -p "$prompt (yes/no): " reply
|
||||
if [[ "$reply" != "yes" ]]; then
|
||||
echo "操作已取消"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
existing_container_id=$(docker ps -a --filter "name=^${CONTAINER_NAME}$" -q)
|
||||
if [[ -n "$existing_container_id" ]]; then
|
||||
echo "[WARN] Container ${CONTAINER_NAME} already exists."
|
||||
confirm_remove "Remove the existing staging container and recreate it?"
|
||||
docker rm -f "$CONTAINER_NAME" >/dev/null
|
||||
fi
|
||||
|
||||
echo "[INFO] Removing existing volume (if any): ${VOLUME_NAME}"
|
||||
if docker volume ls -q --filter "name=^${VOLUME_NAME}$" | grep -Fxq "$VOLUME_NAME"; then
|
||||
docker volume rm "$VOLUME_NAME" >/dev/null
|
||||
fi
|
||||
|
||||
if port_in_use; then
|
||||
containers_on_port=$(docker ps --filter "publish=${HOST_PORT}" --format '{{.ID}}\t{{.Names}}')
|
||||
if [[ -n "$containers_on_port" ]]; then
|
||||
echo "[WARN] Host port ${HOST_PORT} is currently used by the following container(s):"
|
||||
while IFS=$'\t' read -r cid cname; do
|
||||
[[ -z "$cid" ]] && continue
|
||||
echo " - ${cname} (${cid})"
|
||||
done <<<"$containers_on_port"
|
||||
confirm_remove "Stop and remove these container(s)?"
|
||||
while IFS=$'\t' read -r cid cname; do
|
||||
[[ -z "$cid" ]] && continue
|
||||
docker stop "$cid" >/dev/null
|
||||
docker rm "$cid" >/dev/null
|
||||
done <<<"$containers_on_port"
|
||||
else
|
||||
echo "[ERROR] Host port ${HOST_PORT} is already in use. Set STAGING_MYSQL_PORT to an unused port or stop the process using it." >&2
|
||||
exit 1
|
||||
fi
|
||||
if port_in_use; then
|
||||
echo "[ERROR] Host port ${HOST_PORT} is still in use after stopping containers. Please free the port manually or change STAGING_MYSQL_PORT." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "[INFO] Starting fresh MySQL container ${CONTAINER_NAME}"
|
||||
docker run -d \
|
||||
--name "$CONTAINER_NAME" \
|
||||
-p "${HOST_PORT}:3306" \
|
||||
-e MYSQL_ROOT_PASSWORD="$ROOT_PASSWORD" \
|
||||
-e MYSQL_DATABASE="$DB_NAME" \
|
||||
-v "$VOLUME_NAME:/var/lib/mysql" \
|
||||
"$MYSQL_IMAGE" \
|
||||
--lower_case_table_names=1 \
|
||||
--explicit_defaults_for_timestamp=1 \
|
||||
--character-set-server=utf8mb4 \
|
||||
--collation-server=utf8mb4_unicode_ci >/dev/null
|
||||
|
||||
echo -n "[INFO] Waiting for MySQL to accept connections"
|
||||
for attempt in {1..60}; do
|
||||
if docker exec "$CONTAINER_NAME" mysqladmin ping -h127.0.0.1 -uroot -p"$ROOT_PASSWORD" --silent >/dev/null 2>&1; then
|
||||
echo " - ready"
|
||||
break
|
||||
fi
|
||||
sleep 2
|
||||
echo -n "."
|
||||
if [[ $attempt -eq 60 ]]; then
|
||||
echo "\n[ERROR] MySQL did not become ready in time" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
case "$backup_path" in
|
||||
*.gz)
|
||||
reader=(gzip -dc -- "$backup_path")
|
||||
;;
|
||||
*.xz)
|
||||
reader=(xz -dc -- "$backup_path")
|
||||
;;
|
||||
*.zip)
|
||||
reader=(unzip -p -- "$backup_path")
|
||||
;;
|
||||
*)
|
||||
reader=(cat -- "$backup_path")
|
||||
;;
|
||||
esac
|
||||
|
||||
echo "[INFO] Importing backup $backup_path into ${DB_NAME}"
|
||||
"${reader[@]}" | docker exec -i "$CONTAINER_NAME" mysql -uroot -p"$ROOT_PASSWORD" --default-character-set=utf8mb4 "$DB_NAME"
|
||||
|
||||
echo "[INFO] Staging database restored. Container ${CONTAINER_NAME} is listening on port ${HOST_PORT}."
|
||||
Reference in New Issue
Block a user