272 lines
7.6 KiB
Bash
272 lines
7.6 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}"
|
|
remote_folder="${REMOTE_FOLDER:-/opt/www/virtual/SkinbaseNova}"
|
|
remote_server="${REMOTE_SERVER:-klevze@server3.klevze.si}"
|
|
php_bin="${PHP_BIN:-php}"
|
|
composer_bin="${COMPOSER_BIN:-composer}"
|
|
ssh_bin="${SSH_BIN:-ssh}"
|
|
rsync_bin="${RSYNC_BIN:-rsync}"
|
|
local_build_command="${LOCAL_BUILD_COMMAND:-}"
|
|
|
|
run_local_build=1
|
|
run_remote_migrations=1
|
|
run_db_sync=0
|
|
db_sync_source=""
|
|
legacy_db_sync_mode=0
|
|
force_db_sync=0
|
|
skip_maintenance=0
|
|
db_sync_confirm_target="${DB_SYNC_CONFIRM_TARGET:-}"
|
|
db_sync_confirm_phrase="${DB_SYNC_CONFIRM_PHRASE:-}"
|
|
|
|
usage() {
|
|
cat <<'EOF'
|
|
Usage: bash sync.sh [options]
|
|
|
|
Options:
|
|
--skip-build Skip local npm build before rsync.
|
|
--skip-migrate Skip php artisan migrate on the server.
|
|
--with-db-from=local Replace the production database with a dump from the local database.
|
|
--confirm-db-sync-target HOST
|
|
Must match the remote server name when running non-interactively.
|
|
--confirm-db-sync-phrase TEXT
|
|
Must equal 'replace production db from local' when running non-interactively.
|
|
--with-db Legacy alias for --with-db-from=local.
|
|
--force-db-sync Legacy extra confirmation flag for --with-db.
|
|
--no-maintenance Skip php artisan down/up during deploy.
|
|
--help Show this help.
|
|
|
|
Environment overrides:
|
|
LOCAL_FOLDER, REMOTE_FOLDER, REMOTE_SERVER, PHP_BIN, COMPOSER_BIN, SSH_BIN, RSYNC_BIN,
|
|
LOCAL_BUILD_COMMAND, DB_SYNC_CONFIRM_TARGET, DB_SYNC_CONFIRM_PHRASE
|
|
EOF
|
|
}
|
|
|
|
confirm_database_replacement() {
|
|
local expected_phrase="replace production db from local"
|
|
local typed_target=""
|
|
local typed_phrase=""
|
|
|
|
if [[ "$run_db_sync" -ne 1 || "$db_sync_source" != "local" ]]; then
|
|
return
|
|
fi
|
|
|
|
echo "WARNING: this will overwrite the production database on $remote_server using your local database dump."
|
|
|
|
if [[ -n "$db_sync_confirm_target" || -n "$db_sync_confirm_phrase" ]]; then
|
|
if [[ "$db_sync_confirm_target" != "$remote_server" ]]; then
|
|
echo "Refusing DB sync: --confirm-db-sync-target must exactly match $remote_server." >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [[ "$db_sync_confirm_phrase" != "$expected_phrase" ]]; then
|
|
echo "Refusing DB sync: --confirm-db-sync-phrase must exactly equal '$expected_phrase'." >&2
|
|
exit 1
|
|
fi
|
|
|
|
return
|
|
fi
|
|
|
|
if [[ ! -t 0 ]]; then
|
|
echo "Refusing DB sync in non-interactive mode without --confirm-db-sync-target \"$remote_server\" and --confirm-db-sync-phrase \"$expected_phrase\"." >&2
|
|
exit 1
|
|
fi
|
|
|
|
read -r -p "Type the remote server to confirm DB replacement [$remote_server]: " typed_target
|
|
if [[ "$typed_target" != "$remote_server" ]]; then
|
|
echo "Refusing DB sync: remote server confirmation did not match." >&2
|
|
exit 1
|
|
fi
|
|
|
|
read -r -p "Type '$expected_phrase' to continue: " typed_phrase
|
|
if [[ "$typed_phrase" != "$expected_phrase" ]]; then
|
|
echo "Refusing DB sync: confirmation phrase did not match." >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
is_wsl() {
|
|
[[ -n "${WSL_DISTRO_NAME:-}" || -n "${WSL_INTEROP:-}" ]]
|
|
}
|
|
|
|
run_frontend_build() {
|
|
if [[ -n "$local_build_command" ]]; then
|
|
(
|
|
cd "$local_folder"
|
|
eval "$local_build_command"
|
|
)
|
|
return
|
|
fi
|
|
|
|
if is_wsl && command -v wslpath >/dev/null 2>&1 && command -v powershell.exe >/dev/null 2>&1; then
|
|
local windows_local_folder
|
|
windows_local_folder="$(wslpath -w "$local_folder")"
|
|
|
|
echo "Detected WSL checkout; running frontend build with Windows npm.cmd to match local node_modules..."
|
|
powershell.exe -NoProfile -ExecutionPolicy Bypass -Command \
|
|
"Set-Location -LiteralPath '$windows_local_folder'; npm.cmd run build"
|
|
return
|
|
fi
|
|
|
|
(
|
|
cd "$local_folder"
|
|
npm run build
|
|
)
|
|
}
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--skip-build)
|
|
run_local_build=0
|
|
;;
|
|
--skip-migrate)
|
|
run_remote_migrations=0
|
|
;;
|
|
--with-db-from=local)
|
|
run_db_sync=1
|
|
db_sync_source="local"
|
|
;;
|
|
--with-db-from=*)
|
|
echo "Unsupported DB sync source in option: $1" >&2
|
|
echo "Only --with-db-from=local is supported." >&2
|
|
exit 1
|
|
;;
|
|
--with-db)
|
|
run_db_sync=1
|
|
db_sync_source="local"
|
|
legacy_db_sync_mode=1
|
|
;;
|
|
--force-db-sync)
|
|
force_db_sync=1
|
|
;;
|
|
--confirm-db-sync-target)
|
|
shift
|
|
db_sync_confirm_target="${1:?Missing value for --confirm-db-sync-target}"
|
|
;;
|
|
--confirm-db-sync-target=*)
|
|
db_sync_confirm_target="${1#*=}"
|
|
;;
|
|
--confirm-db-sync-phrase)
|
|
shift
|
|
db_sync_confirm_phrase="${1:?Missing value for --confirm-db-sync-phrase}"
|
|
;;
|
|
--confirm-db-sync-phrase=*)
|
|
db_sync_confirm_phrase="${1#*=}"
|
|
;;
|
|
--no-maintenance)
|
|
skip_maintenance=1
|
|
;;
|
|
--help|-h)
|
|
usage
|
|
exit 0
|
|
;;
|
|
*)
|
|
echo "Unknown option: $1" >&2
|
|
usage >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
if [[ "$run_db_sync" -eq 1 && "$db_sync_source" != "local" ]]; then
|
|
echo "Refusing DB sync without an explicit source. Use --with-db-from=local." >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [[ "$run_db_sync" -eq 1 ]] && { [[ -z "$db_sync_confirm_target" && -n "$db_sync_confirm_phrase" ]] || [[ -n "$db_sync_confirm_target" && -z "$db_sync_confirm_phrase" ]]; }; then
|
|
echo "Refusing DB sync: both --confirm-db-sync-target and --confirm-db-sync-phrase are required together when provided." >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [[ "$legacy_db_sync_mode" -eq 1 && "$force_db_sync" -ne 1 ]]; then
|
|
echo "Refusing legacy --with-db without --force-db-sync. Prefer --with-db-from=local instead." >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [[ "$run_db_sync" -eq 1 ]]; then
|
|
confirm_database_replacement
|
|
fi
|
|
|
|
if [[ "$run_local_build" -eq 1 ]]; then
|
|
echo "Building frontend assets locally..."
|
|
run_frontend_build
|
|
fi
|
|
|
|
echo "Syncing application files to $remote_server..."
|
|
"$rsync_bin" -avz \
|
|
--delete \
|
|
--delete-delay \
|
|
--chmod=D755,F644 \
|
|
--exclude ".phpintel/" \
|
|
--exclude "bootstrap/cache/" \
|
|
--exclude ".env" \
|
|
--exclude "public/hot" \
|
|
--exclude "node_modules" \
|
|
--exclude "public/files/" \
|
|
--exclude "resources/lang/" \
|
|
--exclude "storage/" \
|
|
--exclude ".git/" \
|
|
--exclude ".cursor/" \
|
|
--exclude ".venv/" \
|
|
--exclude "/oldSite" \
|
|
--exclude "/vendor" \
|
|
-e ssh \
|
|
"$local_folder/" \
|
|
"$remote_server:$remote_folder/"
|
|
|
|
if [[ "$run_db_sync" -eq 1 ]]; then
|
|
echo "Replacing the production database from the local dump..."
|
|
"$script_dir/push-db-to-prod.sh" \
|
|
--force \
|
|
--remote-server "$remote_server" \
|
|
--remote-folder "$remote_folder" \
|
|
$( [[ "$run_remote_migrations" -eq 0 ]] && printf '%s' '--skip-migrate' )
|
|
fi
|
|
|
|
echo "Running remote Composer and Artisan steps..."
|
|
"$ssh_bin" "$remote_server" \
|
|
REMOTE_FOLDER="$(printf '%q' "$remote_folder")" \
|
|
PHP_BIN="$(printf '%q' "$php_bin")" \
|
|
COMPOSER_BIN="$(printf '%q' "$composer_bin")" \
|
|
RUN_REMOTE_MIGRATIONS="$run_remote_migrations" \
|
|
SKIP_MAINTENANCE="$skip_maintenance" \
|
|
'bash -s' <<'EOF'
|
|
set -euo pipefail
|
|
|
|
cd "$REMOTE_FOLDER"
|
|
|
|
bring_app_up() {
|
|
if [[ "$SKIP_MAINTENANCE" -eq 0 ]]; then
|
|
"$PHP_BIN" artisan up >/dev/null 2>&1 || true
|
|
fi
|
|
}
|
|
|
|
trap bring_app_up EXIT
|
|
|
|
if [[ "$SKIP_MAINTENANCE" -eq 0 ]]; then
|
|
"$PHP_BIN" artisan down --retry=60 || true
|
|
fi
|
|
|
|
"$COMPOSER_BIN" install --no-dev --prefer-dist --optimize-autoloader --no-interaction
|
|
|
|
if [[ "$RUN_REMOTE_MIGRATIONS" -eq 1 ]]; then
|
|
"$PHP_BIN" artisan migrate --force
|
|
fi
|
|
|
|
"$PHP_BIN" artisan optimize:clear
|
|
"$PHP_BIN" artisan config:cache
|
|
"$PHP_BIN" artisan view:cache
|
|
"$PHP_BIN" artisan queue:restart || true
|
|
|
|
if [[ "$SKIP_MAINTENANCE" -eq 0 ]]; then
|
|
"$PHP_BIN" artisan up
|
|
trap - EXIT
|
|
fi
|
|
EOF
|
|
|
|
echo "Deployment complete." |