Verwendung
- LocalWP Site entwickeln
- Dort Site Shell anklicken
- Von dort aus
wp-push sitename - zieht sitename.conf aus /Sites/deployments Ordner
- Migration wird durchgeführt
- Fertig
Wichtig
Füge alles was mit dem Theme zu tun hat in Themes Folder. Also alle svgs, woff2s, images etc.
wp-push.sh
Bash
#!/bin/bash
# ============================================================
# wp-push.sh — Generic WordPress Local → Remote push
#
# Usage:
# ./wp-push.sh <config-file> [options]
#
# Options:
# --no-db Skip DB
# --no-plugins Skip plugins folder
# --no-themes Skip themes folder
# --no-mu-plugins Skip mu-plugins folder
# --no-uploads Skip uploads folder
# --no-languages Skip languages folder (.mo/.po Übersetzungen)
# --plugin=NAME[,NAME] Only sync these plugin folders (no themes/mu/uploads)
# --theme=NAME[,NAME] Only sync these theme folders
# --only-db DB only, no files
# --only-files Files only, no DB
#
# Examples:
# ./wp-push.sh sites/kzyk.conf
# ./wp-push.sh sites/kzyk.conf --plugin=my-plugin # nur Plugin + DB
# ./wp-push.sh sites/kzyk.conf --plugin=my-plugin --no-db # nur Plugin
# ./wp-push.sh sites/kzyk.conf --only-db
# ============================================================
set -euo pipefail
DEPLOYMENTS_DIR="${DEPLOYMENTS_DIR:-$HOME/Sites/deployments}"
ARG="${1:-}"
if [[ -z "$ARG" ]]; then
sed -n '2,25p' "$0"
echo ""
echo "Available configs in $DEPLOYMENTS_DIR:"
shopt -s nullglob
configs=("$DEPLOYMENTS_DIR"/*.conf)
shopt -u nullglob
if (( ${#configs[@]} == 0 )); then
echo " (none)"
else
for c in "${configs[@]}"; do
name="$(basename "$c" .conf)"
echo " - $name"
done
fi
exit 1
fi
shift
# Wenn ARG eine existierende Datei ist → direkt nehmen, sonst in DEPLOYMENTS_DIR suchen
if [[ -f "$ARG" ]]; then
CONFIG_FILE="$ARG"
elif [[ -f "$DEPLOYMENTS_DIR/$ARG.conf" ]]; then
CONFIG_FILE="$DEPLOYMENTS_DIR/$ARG.conf"
elif [[ -f "$DEPLOYMENTS_DIR/$ARG" ]]; then
CONFIG_FILE="$DEPLOYMENTS_DIR/$ARG"
else
echo "❌ Config nicht gefunden: $ARG"
echo " Gesucht in: $DEPLOYMENTS_DIR/$ARG.conf"
exit 1
fi
echo "📋 Config: $CONFIG_FILE"
# shellcheck disable=SC1090
source "$CONFIG_FILE"
for v in LOCAL_PATH REMOTE_USER REMOTE_HOST REMOTE_PATH REMOTE_URL REMOTE_PREFIX; do
[[ -z "${!v:-}" ]] && { echo "❌ Missing in config: $v"; exit 1; }
done
# Optional mit Defaults / Auto-Detect
LOCAL_PREFIX="${LOCAL_PREFIX:-wp_}"
if [[ -z "${LOCAL_URL:-}" ]]; then
LOCAL_URL="$(wp --path="$LOCAL_PATH" option get home 2>/dev/null || true)"
[[ -z "$LOCAL_URL" ]] && { echo "❌ LOCAL_URL nicht erkannt (kein wp option get home). In conf setzen."; exit 1; }
echo "🔎 LOCAL_URL auto-detect: $LOCAL_URL"
fi
# Defaults: alles syncen
SYNC_DB=true
SYNC_PLUGINS=true
SYNC_THEMES=true
SYNC_MU_PLUGINS=true
SYNC_UPLOADS=true
SYNC_LANGUAGES=true
PLUGIN_FILTER=""
THEME_FILTER=""
while [[ $# -gt 0 ]]; do
case "$1" in
--no-db) SYNC_DB=false ;;
--no-plugins) SYNC_PLUGINS=false ;;
--no-themes) SYNC_THEMES=false ;;
--no-mu-plugins) SYNC_MU_PLUGINS=false ;;
--no-uploads) SYNC_UPLOADS=false ;;
--no-languages) SYNC_LANGUAGES=false ;;
--only-db) SYNC_PLUGINS=false; SYNC_THEMES=false; SYNC_MU_PLUGINS=false; SYNC_UPLOADS=false; SYNC_LANGUAGES=false ;;
--only-files) SYNC_DB=false ;;
--plugin=*) PLUGIN_FILTER="${1#--plugin=}"; SYNC_PLUGINS=true; SYNC_THEMES=false; SYNC_MU_PLUGINS=false; SYNC_UPLOADS=false; SYNC_LANGUAGES=false ;;
--theme=*) THEME_FILTER="${1#--theme=}"; SYNC_THEMES=true; SYNC_PLUGINS=false; SYNC_MU_PLUGINS=false; SYNC_UPLOADS=false; SYNC_LANGUAGES=false ;;
*) echo "❌ Unknown option: $1"; exit 1 ;;
esac
shift
done
DB_FILE="/tmp/wp-push-$$.sql"
SSH_CTRL="/tmp/ssh-ctrl-$$-%h-%p-%r"
SSH_OPTS_STR="-o ControlMaster=auto -o ControlPath=$SSH_CTRL -o ControlPersist=10m"
# shellcheck disable=SC2206
SSH_OPTS=($SSH_OPTS_STR)
SSH_TARGET="$REMOTE_USER@$REMOTE_HOST"
cleanup() {
rm -f "$DB_FILE" "$DB_FILE.bak"
ssh "${SSH_OPTS[@]}" -O exit "$SSH_TARGET" 2>/dev/null || true
}
trap cleanup EXIT
wp_local() { wp --path="$LOCAL_PATH" "$@"; }
wp_remote() { ssh "${SSH_OPTS[@]}" "$SSH_TARGET" "wp --path='$REMOTE_PATH' $*"; }
ssh_run() { ssh "${SSH_OPTS[@]}" "$SSH_TARGET" "$@"; }
# Sync ein wp-content/<dir>. Wenn $2 (filter) gesetzt: nur Subfolder aus CSV-Liste.
sync_dir() {
local dir="$1" filter="${2:-}"
local src="$LOCAL_PATH/wp-content/$dir"
[[ ! -d "$src" ]] && return 0
if [[ -z "$filter" ]]; then
echo " → $dir"
rsync -azu -e "ssh $SSH_OPTS_STR" \
"$src/" "$SSH_TARGET:$REMOTE_PATH/wp-content/$dir/"
else
IFS=',' read -ra items <<< "$filter"
for item in "${items[@]}"; do
if [[ -e "$src/$item" ]]; then
echo " → $dir/$item"
rsync -azu -e "ssh $SSH_OPTS_STR" \
"$src/$item" "$SSH_TARGET:$REMOTE_PATH/wp-content/$dir/"
else
echo " ⚠️ $dir/$item nicht gefunden, übersprungen"
fi
done
fi
}
echo "🚀 Push: $LOCAL_URL → $REMOTE_URL"
echo ""
# ------------------------------------------------------------
# DB
# ------------------------------------------------------------
if $SYNC_DB; then
echo "📦 DB exportieren..."
TABLES="$(wp_local db tables --all-tables-with-prefix --format=csv)"
wp_local db export "$DB_FILE" --tables="$TABLES" --add-drop-table
if [[ "$LOCAL_PREFIX" != "$REMOTE_PREFIX" ]]; then
echo " Tabellennamen umschreiben: ${LOCAL_PREFIX} → ${REMOTE_PREFIX}"
sed -i.bak "s/\`${LOCAL_PREFIX}/\`${REMOTE_PREFIX}/g" "$DB_FILE"
rm -f "$DB_FILE.bak"
fi
echo "📤 DB importieren..."
ssh "${SSH_OPTS[@]}" "$SSH_TARGET" "wp --path='$REMOTE_PATH' db import -" < "$DB_FILE"
if [[ "$LOCAL_PREFIX" != "$REMOTE_PREFIX" ]]; then
echo "🔧 Prefix in option_name + meta_key korrigieren..."
# BINARY + exakter Prefix-Match (case-sensitive, kein LIKE-Wildcard).
# Sonst matcht z.B. 'wp_%' auch WPLANG, wpforms_*, wpseo_* — und zerstört sie.
PFLEN=${#LOCAL_PREFIX}
wp_remote "db query \"UPDATE ${REMOTE_PREFIX}options SET option_name = CONCAT('${REMOTE_PREFIX}', SUBSTRING(option_name, $((PFLEN+1)))) WHERE BINARY SUBSTRING(option_name, 1, ${PFLEN}) = '${LOCAL_PREFIX}'\""
wp_remote "db query \"UPDATE ${REMOTE_PREFIX}usermeta SET meta_key = CONCAT('${REMOTE_PREFIX}', SUBSTRING(meta_key, $((PFLEN+1)))) WHERE BINARY SUBSTRING(meta_key, 1, ${PFLEN}) = '${LOCAL_PREFIX}'\""
fi
echo "🔁 URLs ersetzen: $LOCAL_URL → $REMOTE_URL"
wp_remote "search-replace '$LOCAL_URL' '$REMOTE_URL' --skip-columns=guid --all-tables"
else
echo "⏭ DB übersprungen"
fi
# ------------------------------------------------------------
# Files
# ------------------------------------------------------------
echo "📁 Files synchronisieren..."
$SYNC_PLUGINS && sync_dir plugins "$PLUGIN_FILTER"
$SYNC_THEMES && sync_dir themes "$THEME_FILTER"
$SYNC_MU_PLUGINS && sync_dir mu-plugins
$SYNC_UPLOADS && sync_dir uploads
$SYNC_LANGUAGES && sync_dir languages
true # damit set -e nicht bei "false &&" abbricht
# ------------------------------------------------------------
# Flush (nur sinnvoll wenn DB oder Plugins/Themes aktualisiert)
# ------------------------------------------------------------
if $SYNC_DB || $SYNC_PLUGINS || $SYNC_THEMES || $SYNC_MU_PLUGINS; then
echo "🧹 Rewrite + Cache flush..."
wp_remote "rewrite flush"
wp_remote "cache flush"
ssh_run "wp --path='$REMOTE_PATH' litespeed-purge all 2>/dev/null || true"
fi
echo ""
echo "✅ Fertig! $REMOTE_URL"demo.conf
Bash
# Site config für wp-push.sh
# Verwendung: wp-push sitename
LOCAL_PATH=~/Sites/localwp/sitename/app/public
LOCAL_URL=https://sitename.local
LOCAL_PREFIX=wp_
REMOTE_USER=SSH-USERNAME
REMOTE_HOST=HOST
REMOTE_PATH=/home/SSH-USERNAME/public_html/sitename.gwsite.ch
REMOTE_URL=https://sitename.gwsite.ch
REMOTE_PREFIX=sitename_
.zshrc
Bash
alias wp-push="~/Sites/deployments/wp-push.sh"