284 lines
8.0 KiB
Bash
284 lines
8.0 KiB
Bash
#!/bin/bash
|
|
set -euo pipefail
|
|
|
|
script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
|
root_dir="$(cd -- "$script_dir/.." && pwd)"
|
|
|
|
local_folder="${LOCAL_FOLDER:-$root_dir}"
|
|
local_env_file="${LOCAL_ENV_FILE:-$local_folder/.env}"
|
|
remote_folder="${REMOTE_FOLDER:-/opt/www/virtual/SkinbaseNova}"
|
|
remote_server="${REMOTE_SERVER:-klevze@server3.klevze.si}"
|
|
remote_env_file="${REMOTE_ENV_FILE:-$remote_folder/.env}"
|
|
php_bin="${PHP_BIN:-php}"
|
|
ssh_bin="${SSH_BIN:-ssh}"
|
|
scp_bin="${SCP_BIN:-scp}"
|
|
local_mysqldump_command="${LOCAL_MYSQLDUMP_COMMAND:-}"
|
|
|
|
force_replace=0
|
|
skip_remote_backup=0
|
|
run_remote_migrate=1
|
|
|
|
usage() {
|
|
cat <<'EOF'
|
|
Usage: bash scripts/push-db-to-prod.sh --force [options]
|
|
|
|
Options:
|
|
--force Required. Confirms that production DB replacement is intentional.
|
|
--skip-remote-backup Skip taking a production backup before import.
|
|
--skip-migrate Skip php artisan migrate after import.
|
|
--remote-server HOST Override the SSH destination.
|
|
--remote-folder PATH Override the remote app path.
|
|
--local-env-file PATH Override the local Laravel .env path.
|
|
--remote-env-file PATH Override the remote Laravel .env path.
|
|
--help Show this help.
|
|
|
|
This script replaces the remote application database with a dump created from the local database.
|
|
EOF
|
|
}
|
|
|
|
is_wsl() {
|
|
[[ -n "${WSL_DISTRO_NAME:-}" || -n "${WSL_INTEROP:-}" ]]
|
|
}
|
|
|
|
use_windows_local_mysql_client() {
|
|
if [[ -n "$local_mysqldump_command" ]]; then
|
|
return 1
|
|
fi
|
|
|
|
if ! is_wsl; then
|
|
return 1
|
|
fi
|
|
|
|
if [[ "$local_db_host" != "127.0.0.1" && "$local_db_host" != "localhost" ]]; then
|
|
return 1
|
|
fi
|
|
|
|
command -v powershell.exe >/dev/null 2>&1 && command -v wslpath >/dev/null 2>&1
|
|
}
|
|
|
|
run_local_dump() {
|
|
local uncompressed_dump_file="${local_dump_file%.gz}"
|
|
|
|
if [[ -n "$local_mysqldump_command" ]]; then
|
|
echo "Creating local database dump with LOCAL_MYSQLDUMP_COMMAND..."
|
|
(
|
|
cd "$local_folder"
|
|
eval "$local_mysqldump_command"
|
|
) | gzip > "$local_dump_file"
|
|
return
|
|
fi
|
|
|
|
if use_windows_local_mysql_client; then
|
|
local windows_dump_file
|
|
local escaped_password
|
|
local escaped_host
|
|
local escaped_port
|
|
local escaped_user
|
|
local escaped_name
|
|
|
|
windows_dump_file="$(wslpath -w "$uncompressed_dump_file")"
|
|
escaped_password="${local_db_password//\'/\'\'}"
|
|
escaped_host="${local_db_host//\'/\'\'}"
|
|
escaped_port="${local_db_port//\'/\'\'}"
|
|
escaped_user="${local_db_user//\'/\'\'}"
|
|
escaped_name="${local_db_name//\'/\'\'}"
|
|
|
|
echo "Detected WSL with a Windows-local MySQL host; running local dump with Windows mysqldump.exe..."
|
|
powershell.exe -NoProfile -ExecutionPolicy Bypass -Command \
|
|
"\$ErrorActionPreference = 'Stop'; \
|
|
\$mysqldump = (Get-Command mysqldump.exe).Source; \
|
|
if (-not \$mysqldump) { throw 'mysqldump.exe not found on Windows PATH.' } \
|
|
\$env:MYSQL_PWD = '$escaped_password'; \
|
|
\$arguments = @('--host=$escaped_host', '--port=$escaped_port', '--user=$escaped_user', '--single-transaction', '--quick', '--routines', '--triggers', '--hex-blob', '--no-tablespaces', '--default-character-set=utf8mb4', '$escaped_name'); \
|
|
\$process = Start-Process -FilePath \$mysqldump -ArgumentList \$arguments -NoNewWindow -RedirectStandardOutput '$windows_dump_file' -Wait -PassThru; \
|
|
if (\$process.ExitCode -ne 0) { exit \$process.ExitCode }"
|
|
|
|
gzip -f "$uncompressed_dump_file"
|
|
return
|
|
fi
|
|
|
|
echo "Creating local database dump from $local_db_name..."
|
|
MYSQL_PWD="$local_db_password" mysqldump \
|
|
--host="$local_db_host" \
|
|
--port="$local_db_port" \
|
|
--user="$local_db_user" \
|
|
--single-transaction \
|
|
--quick \
|
|
--routines \
|
|
--triggers \
|
|
--hex-blob \
|
|
--no-tablespaces \
|
|
--default-character-set=utf8mb4 \
|
|
"$local_db_name" | gzip > "$local_dump_file"
|
|
}
|
|
|
|
read_env_value() {
|
|
local key="$1"
|
|
local file="$2"
|
|
local line
|
|
|
|
line="$(grep -E "^${key}=" "$file" | tail -n 1 || true)"
|
|
line="${line#*=}"
|
|
line="${line%$'\r'}"
|
|
line="${line#\"}"
|
|
line="${line%\"}"
|
|
line="${line#\'}"
|
|
line="${line%\'}"
|
|
printf '%s' "$line"
|
|
}
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--force)
|
|
force_replace=1
|
|
;;
|
|
--skip-remote-backup)
|
|
skip_remote_backup=1
|
|
;;
|
|
--skip-migrate)
|
|
run_remote_migrate=0
|
|
;;
|
|
--remote-server)
|
|
shift
|
|
remote_server="${1:?Missing value for --remote-server}"
|
|
;;
|
|
--remote-folder)
|
|
shift
|
|
remote_folder="${1:?Missing value for --remote-folder}"
|
|
remote_env_file="$remote_folder/.env"
|
|
;;
|
|
--local-env-file)
|
|
shift
|
|
local_env_file="${1:?Missing value for --local-env-file}"
|
|
;;
|
|
--remote-env-file)
|
|
shift
|
|
remote_env_file="${1:?Missing value for --remote-env-file}"
|
|
;;
|
|
--help|-h)
|
|
usage
|
|
exit 0
|
|
;;
|
|
*)
|
|
echo "Unknown option: $1" >&2
|
|
usage >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
if [[ "$force_replace" -ne 1 ]]; then
|
|
echo "Refusing to replace the production database without --force." >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [[ ! -f "$local_env_file" ]]; then
|
|
echo "Local env file not found: $local_env_file" >&2
|
|
exit 1
|
|
fi
|
|
|
|
local_db_host="$(read_env_value DB_HOST "$local_env_file")"
|
|
local_db_port="$(read_env_value DB_PORT "$local_env_file")"
|
|
local_db_name="$(read_env_value DB_DATABASE "$local_env_file")"
|
|
local_db_user="$(read_env_value DB_USERNAME "$local_env_file")"
|
|
local_db_password="$(read_env_value DB_PASSWORD "$local_env_file")"
|
|
|
|
if [[ -z "$local_db_host" || -z "$local_db_port" || -z "$local_db_name" || -z "$local_db_user" ]]; then
|
|
echo "Local database settings are incomplete in $local_env_file" >&2
|
|
exit 1
|
|
fi
|
|
|
|
local_dump_file="$(mktemp "${TMPDIR:-/tmp}/skinbase-prod-sync-XXXXXX.sql.gz")"
|
|
remote_dump_file="/tmp/$(basename "$local_dump_file")"
|
|
|
|
cleanup() {
|
|
rm -f "$local_dump_file"
|
|
rm -f "${local_dump_file%.gz}"
|
|
}
|
|
|
|
trap cleanup EXIT
|
|
|
|
run_local_dump
|
|
|
|
echo "Uploading dump to $remote_server..."
|
|
"$scp_bin" "$local_dump_file" "$remote_server:$remote_dump_file"
|
|
|
|
echo "Importing dump on the production server..."
|
|
"$ssh_bin" "$remote_server" \
|
|
REMOTE_FOLDER="$(printf '%q' "$remote_folder")" \
|
|
REMOTE_ENV_FILE="$(printf '%q' "$remote_env_file")" \
|
|
REMOTE_DUMP_FILE="$(printf '%q' "$remote_dump_file")" \
|
|
PHP_BIN="$(printf '%q' "$php_bin")" \
|
|
SKIP_REMOTE_BACKUP="$skip_remote_backup" \
|
|
RUN_REMOTE_MIGRATE="$run_remote_migrate" \
|
|
'bash -s' <<'EOF'
|
|
set -euo pipefail
|
|
|
|
read_env_value() {
|
|
local key="$1"
|
|
local file="$2"
|
|
local line
|
|
|
|
line="$(grep -E "^${key}=" "$file" | tail -n 1 || true)"
|
|
line="${line#*=}"
|
|
line="${line%$'\r'}"
|
|
line="${line#\"}"
|
|
line="${line%\"}"
|
|
line="${line#\'}"
|
|
line="${line%\'}"
|
|
printf '%s' "$line"
|
|
}
|
|
|
|
if [[ ! -f "$REMOTE_ENV_FILE" ]]; then
|
|
echo "Remote env file not found: $REMOTE_ENV_FILE" >&2
|
|
exit 1
|
|
fi
|
|
|
|
db_host="$(read_env_value DB_HOST "$REMOTE_ENV_FILE")"
|
|
db_port="$(read_env_value DB_PORT "$REMOTE_ENV_FILE")"
|
|
db_name="$(read_env_value DB_DATABASE "$REMOTE_ENV_FILE")"
|
|
db_user="$(read_env_value DB_USERNAME "$REMOTE_ENV_FILE")"
|
|
db_password="$(read_env_value DB_PASSWORD "$REMOTE_ENV_FILE")"
|
|
|
|
if [[ -z "$db_host" || -z "$db_port" || -z "$db_name" || -z "$db_user" ]]; then
|
|
echo "Remote database settings are incomplete in $REMOTE_ENV_FILE" >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [[ "$SKIP_REMOTE_BACKUP" -eq 0 ]]; then
|
|
backup_dir="$REMOTE_FOLDER/storage/app/deploy-backups"
|
|
backup_file="$backup_dir/prod-before-sync-$(date +%Y%m%d-%H%M%S).sql.gz"
|
|
mkdir -p "$backup_dir"
|
|
|
|
echo "Creating production backup at $backup_file..."
|
|
MYSQL_PWD="$db_password" mysqldump \
|
|
--host="$db_host" \
|
|
--port="$db_port" \
|
|
--user="$db_user" \
|
|
--single-transaction \
|
|
--quick \
|
|
--routines \
|
|
--triggers \
|
|
--hex-blob \
|
|
--no-tablespaces \
|
|
--default-character-set=utf8mb4 \
|
|
"$db_name" | gzip > "$backup_file"
|
|
fi
|
|
|
|
echo "Replacing production database $db_name..."
|
|
gunzip -c "$REMOTE_DUMP_FILE" | MYSQL_PWD="$db_password" mysql \
|
|
--host="$db_host" \
|
|
--port="$db_port" \
|
|
--user="$db_user" \
|
|
"$db_name"
|
|
|
|
rm -f "$REMOTE_DUMP_FILE"
|
|
|
|
if [[ "$RUN_REMOTE_MIGRATE" -eq 1 ]]; then
|
|
cd "$REMOTE_FOLDER"
|
|
"$PHP_BIN" artisan migrate --force
|
|
fi
|
|
EOF
|
|
|
|
echo "Production database sync complete." |