#!/bin/bash # # Wrapper script for WP-CLI's search-replace command. # Safely runs 'wp search-replace' with the correct user and path context. # # --- Configuration --- set -e set -u set -o pipefail # Colors for output messages RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # --- Default Values --- WP_ROOT="/var/www/webroot/ROOT" # Default WordPress root directory WEB_USER="litespeed" # Default web server user WP_CLI_PATH="/usr/local/bin/wp" # Default path to WP-CLI # --- Helper Functions --- # Print informational messages info() { printf "${BLUE}[INFO] %s${NC}\n" "$@" } # Print success messages success() { printf "${GREEN}[SUCCESS] %s${NC}\n" "$@" } # Print warning messages warning() { printf "${YELLOW}[WARNING] %s${NC}\n" "$@" } # Print error messages and exit error_exit() { printf "${RED}[ERROR] %s${NC}\n" "$@" >&2 exit 1 } # Function to display usage information usage() { printf "Usage: %s [options...]\n" "$0" printf "\n" printf "A wrapper for the 'wp search-replace' command that automatically handles user and path context.\n" printf "All options after are passed directly to 'wp search-replace'.\n" printf "\n" printf "Required Arguments:\n" printf " The string to search for.\n" printf " The string to replace it with.\n" printf "\n" printf "Common WP-CLI Options (pass these after ):\n" printf " [...] One or more database tables to restrict the search to.\n" printf " --dry-run Run the search-replace without making any changes.\n" printf " --all-tables Search through all tables registered to \$wpdb.\n" printf " --network Search and replace through all sites in a multisite network.\n" printf " --recurse-objects Enable recursing into objects to replace strings.\n" printf " --report-changed-only Only report changes, not every table checked.\n" printf " --skip-columns= Comma-separated list of columns to skip.\n" printf "\n" printf "Wrapper-Specific Options:\n" printf " --wproot=PATH Override the default WordPress root directory (%s).\n" "$WP_ROOT" printf " --webuser=USER Override the default web server user (%s).\n" "$WEB_USER" printf " -h, --help Display this help message.\n" printf "\n" printf "Example:\n" printf " %s 'http://old-domain.com' 'https://new-domain.com' --all-tables --dry-run\n" "$0" exit 1 } # --- Argument Parsing --- # Separate wrapper arguments from WP-CLI arguments WP_CLI_ARGS=() while [[ $# -gt 0 ]]; do case "$1" in -h|--help) usage ;; --wproot=*) WP_ROOT="${1#*=}" shift ;; --webuser=*) WEB_USER="${1#*=}" shift ;; *) # Collect all other arguments for WP-CLI WP_CLI_ARGS+=("$1") shift ;; esac done # Check for mandatory arguments if [[ ${#WP_CLI_ARGS[@]} -lt 2 ]]; then error_exit "Missing required and arguments." fi info "Wrapper settings: WP_ROOT='${WP_ROOT}', WEB_USER='${WEB_USER}'" info "Arguments passed to WP-CLI: ${WP_CLI_ARGS[*]}" # --- Prerequisite Checks --- info "Checking prerequisites..." if [[ ! -d "$WP_ROOT" ]]; then error_exit "WordPress root directory '$WP_ROOT' does not exist."; fi if ! command -v "$WP_CLI_PATH" &> /dev/null; then warning "WP-CLI not found at $WP_CLI_PATH." if ! command -v "wp" &> /dev/null; then error_exit "WP-CLI not found at $WP_CLI_PATH or in system PATH. Please install WP-CLI." else WP_CLI_PATH="wp" # Fallback to wp in path info "Found 'wp' in system PATH. Using that." fi fi if ! id "$WEB_USER" &>/dev/null; then error_exit "Web user '$WEB_USER' does not exist."; fi success "Prerequisites seem OK." # --- WP-CLI Execution Context Setup --- # This logic is adapted from install-wordpress.sh to ensure commands run as the correct user. WP_RUN_ARGS=("--path=$WP_ROOT") SUDO_CMD="" WP_EXECUTABLE="$WP_CLI_PATH" if [[ "$(id -u)" -eq 0 ]]; then info "Script is running as root. Attempting to run WP-CLI as '$WEB_USER'." # Use 'sudo -n' to check for passwordless sudo access. if sudo -n -u "$WEB_USER" "$WP_EXECUTABLE" --info --skip-update --quiet "${WP_RUN_ARGS[@]}" &>/dev/null; then SUDO_CMD="sudo -u $WEB_USER" info "WP-CLI will be executed as '$WEB_USER'." else warning "Could not execute WP-CLI as '$WEB_USER' without a password. Falling back to running as root." WP_RUN_ARGS+=("--allow-root") fi else if [[ "$(id -un)" != "$WEB_USER" ]]; then info "Script is running as non-root user '$(id -un)'. Checking for sudo access to run as '$WEB_USER'." if sudo -n -u "$WEB_USER" "$WP_EXECUTABLE" --info --skip-update --quiet "${WP_RUN_ARGS[@]}" &>/dev/null; then SUDO_CMD="sudo -u $WEB_USER" info "Successfully configured sudo execution for WP-CLI as '$WEB_USER'." else error_exit "Unable to execute WP-CLI as '$WEB_USER'. Ensure the current user has passwordless sudo access, or run this script as '$WEB_USER' or 'root'." fi else info "Script is already running as the web user ('$WEB_USER')." fi fi info "WP-CLI final execution command prefix: [${SUDO_CMD:-direct}]" # --- Domain normalization helper --- trim_trailing_slash() { local str="$1" [[ "$str" == */ ]] && str="${str%/}" printf '%s' "$str" } OLD_URL="${WP_CLI_ARGS[0]}" NEW_URL="${WP_CLI_ARGS[1]}" OLD_NORMAL=$(trim_trailing_slash "$OLD_URL") NEW_NORMAL=$(trim_trailing_slash "$NEW_URL") # Array of replacement tasks (each element "old|||new") TASKS=("$OLD_URL|||$NEW_URL") if [[ "$OLD_URL" != "$OLD_NORMAL" ]]; then # also replace variant without trailing slash TASKS+=("$OLD_NORMAL|||$NEW_NORMAL") fi # --- Execute Command --- info "Executing 'wp search-replace'..." # Build command array safely to preserve argument quoting CMD=() if [[ -n "$SUDO_CMD" ]]; then # shellcheck disable=SC2206 CMD=($SUDO_CMD) # split sudo command into array elements fi CMD+=("$WP_EXECUTABLE" "search-replace") CMD+=("${WP_CLI_ARGS[@]}") CMD+=("${WP_RUN_ARGS[@]}") for pair in "${TASKS[@]}"; do OLD_PART=${pair%%|||*} NEW_PART=${pair##*|||} info "Running wp search-replace '$OLD_PART' '$NEW_PART' …" # Build command anew for each iteration CMD=() if [[ -n "$SUDO_CMD" ]]; then # shellcheck disable=SC2206 CMD=($SUDO_CMD) fi CMD+=("$WP_EXECUTABLE" "search-replace" "$OLD_PART" "$NEW_PART" "${WP_RUN_ARGS[@]}") SEARCH_OUTPUT=$( "${CMD[@]}" 2>&1 ) STATUS=$? printf "%s\n" "$SEARCH_OUTPUT" if [[ $STATUS -ne 0 ]]; then error_exit "wp search-replace failed for pattern '$OLD_PART' -> '$NEW_PART' (exit $STATUS)." fi done success "All search-replace tasks completed successfully." # ----------------------------------------------------------------- # Update wp-config.php WP_HOME and WP_SITEURL if they exist # ----------------------------------------------------------------- CONFIG_FILE="$WP_ROOT/wp-config.php" if [[ -f "$CONFIG_FILE" && -w "$CONFIG_FILE" ]]; then info "Updating WP_HOME and WP_SITEURL in wp-config.php …" # Use the new URL without trailing slash, ensure it has https:// TARGET_URL="$NEW_NORMAL" # Ensure URL starts with https:// if [[ ! "$TARGET_URL" =~ ^https:// ]]; then TARGET_URL="https://$TARGET_URL" info "Added https:// prefix to URL: $TARGET_URL" fi # shellcheck disable=SC2016 # we want literal single quotes inside sed replacement sed -i -E "s|define\(\s*'WP_HOME',\s*'[^']*'\s*\);|define( 'WP_HOME', '${TARGET_URL}' );|; s|define\(\s*'WP_SITEURL',\s*'[^']*'\s*\);|define( 'WP_SITEURL', '${TARGET_URL}' );|" "$CONFIG_FILE" || warning "Failed to update wp-config.php" else warning "wp-config.php not found or not writable; skipped WP_HOME / WP_SITEURL update." fi # Purge LiteSpeed Cache (if plugin installed) info "Attempting LiteSpeed Cache purge (wp litespeed-purge all)…" LS_CMD=() if [[ -n "$SUDO_CMD" ]]; then # shellcheck disable=SC2206 LS_CMD=($SUDO_CMD) fi LS_CMD+=("$WP_EXECUTABLE" "litespeed-purge" "all" "${WP_RUN_ARGS[@]}") "${LS_CMD[@]}" || warning "LiteSpeed purge returned non-zero exit status (plugin may be inactive)." # Delete all transients – recommended after domain/URL migration info "Deleting all transients (wp transient delete --all)..." TRANS_CMD=() if [[ -n "$SUDO_CMD" ]]; then # shellcheck disable=SC2206 TRANS_CMD=($SUDO_CMD) fi TRANS_CMD+=("$WP_EXECUTABLE" "transient" "delete" "--all" "${WP_RUN_ARGS[@]}") "${TRANS_CMD[@]}" || warning "Transient delete command returned non-zero exit status." # Flush object/cache to ensure changes propagate immediately info "Flushing WordPress caches (wp cache flush)..." FLUSH_CMD=() if [[ -n "$SUDO_CMD" ]]; then # shellcheck disable=SC2206 FLUSH_CMD=($SUDO_CMD) fi FLUSH_CMD+=("$WP_EXECUTABLE" "cache" "flush" "${WP_RUN_ARGS[@]}") "${FLUSH_CMD[@]}" || warning "Cache flush command returned non-zero exit status." exit $STATUS