mirror of
https://hk.gh-proxy.com/https://github.com/CaptainCore/do.git
synced 2025-10-03 23:34:10 +08:00
🚀 RELEASE: v1.4
This commit is contained in:
parent
83467aae00
commit
046239a761
3 changed files with 790 additions and 155 deletions
757
_do.sh
757
_do.sh
|
@ -8,7 +8,7 @@
|
||||||
# ----------------------------------------------------
|
# ----------------------------------------------------
|
||||||
|
|
||||||
# --- Global Variables ---
|
# --- Global Variables ---
|
||||||
CAPTAINCORE_DO_VERSION="1.3"
|
CAPTAINCORE_DO_VERSION="1.4"
|
||||||
GUM_VERSION="0.14.4"
|
GUM_VERSION="0.14.4"
|
||||||
CWEBP_VERSION="1.5.0"
|
CWEBP_VERSION="1.5.0"
|
||||||
RCLONE_VERSION="1.69.3"
|
RCLONE_VERSION="1.69.3"
|
||||||
|
@ -18,6 +18,7 @@ CWEBP_CMD=""
|
||||||
IDENTIFY_CMD=""
|
IDENTIFY_CMD=""
|
||||||
WP_CLI_CMD=""
|
WP_CLI_CMD=""
|
||||||
RESTIC_CMD=""
|
RESTIC_CMD=""
|
||||||
|
DISEMBARK_CMD=""
|
||||||
|
|
||||||
# --- Helper Functions ---
|
# --- Helper Functions ---
|
||||||
|
|
||||||
|
@ -120,6 +121,115 @@ function _get_private_dir() {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# ----------------------------------------------------
|
||||||
|
# Checks for and installs 'disembark' if not present.
|
||||||
|
# Prioritizes standard PATH, then a DISEMBARK_DEV_PATH env var.
|
||||||
|
# ----------------------------------------------------
|
||||||
|
function setup_disembark() {
|
||||||
|
# Return if already found
|
||||||
|
if [[ -n "$DISEMBARK_CMD" ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 1. Check if 'disembark' is in the standard PATH
|
||||||
|
if command -v disembark &> /dev/null; then
|
||||||
|
echo " - Found 'disembark' in system PATH." >&2
|
||||||
|
DISEMBARK_CMD="disembark"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 2. Check for a user-defined development path via environment variable
|
||||||
|
if [[ -n "$DISEMBARK_DEV_PATH" && -f "$DISEMBARK_DEV_PATH" && -x "$DISEMBARK_DEV_PATH" ]]; then
|
||||||
|
echo " - Found development version of 'disembark' via DISEMBARK_DEV_PATH." >&2
|
||||||
|
DISEMBARK_CMD="$DISEMBARK_DEV_PATH"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 3. If not found, attempt to install it system-wide
|
||||||
|
echo " Required tool 'disembark' not found. Attempting to install system-wide..." >&2
|
||||||
|
|
||||||
|
# Check for dependencies needed for the installation
|
||||||
|
if ! command -v wget &>/dev/null || ! command -v sudo &>/dev/null; then
|
||||||
|
echo " Error: 'wget' and 'sudo' are required to install disembark." >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
local temp_file
|
||||||
|
temp_file=$(mktemp)
|
||||||
|
|
||||||
|
echo " - Downloading disembark.phar..."
|
||||||
|
if ! wget -q "https://github.com/DisembarkHost/disembark-cli/raw/main/disembark.phar" -O "$temp_file"; then
|
||||||
|
echo " Error: Failed to download disembark.phar." >&2
|
||||||
|
rm -f "$temp_file"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
chmod +x "$temp_file"
|
||||||
|
|
||||||
|
echo " - Moving to /usr/local/bin/disembark. You may be prompted for your password." >&2
|
||||||
|
if ! sudo mv "$temp_file" /usr/local/bin/disembark; then
|
||||||
|
echo " Error: Failed to move disembark.phar to /usr/local/bin/." >&2
|
||||||
|
rm -f "$temp_file"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo " 'disembark' installed successfully." >&2
|
||||||
|
DISEMBARK_CMD="/usr/local/bin/disembark"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# ----------------------------------------------------
|
||||||
|
# Checks for and installs Playwright and its browser dependencies.
|
||||||
|
# Sets a global variable PLAYWRIGHT_READY on success.
|
||||||
|
# ----------------------------------------------------
|
||||||
|
function setup_playwright() {
|
||||||
|
# Return if already checked and ready
|
||||||
|
if [[ -n "$PLAYWRIGHT_READY" ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Pre-flight Checks ---
|
||||||
|
if ! command -v node &>/dev/null; then
|
||||||
|
echo "❌ Error: Node.js is required for Playwright." >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
if ! command -v npm &>/dev/null; then
|
||||||
|
echo "❌ Error: npm is required to install Playwright." >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Check for Browser Executable ---
|
||||||
|
# This is a more robust check. require('playwright') can succeed,
|
||||||
|
# but executablePath() will fail if browsers aren't installed.
|
||||||
|
if node -e "require('playwright').chromium.executablePath()" >/dev/null 2>&1; then
|
||||||
|
echo " - ✅ Playwright and required browsers are already installed."
|
||||||
|
PLAYWRIGHT_READY=true
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Installation ---
|
||||||
|
echo " - ⚠️ Playwright or its browsers are missing. Attempting to install now..."
|
||||||
|
echo " This may take a few minutes as it downloads the browser binaries."
|
||||||
|
|
||||||
|
# Step 1: Install the NPM package
|
||||||
|
if ! npm install playwright; then
|
||||||
|
echo "❌ Error: Failed to install the Playwright npm package." >&2
|
||||||
|
echo " Please try running 'npm install playwright' manually in this directory." >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Step 2: Install the browser binaries (specifically chromium)
|
||||||
|
if ! npx playwright install chromium; then
|
||||||
|
echo "❌ Error: Failed to download Playwright's browser binaries." >&2
|
||||||
|
echo " Please try running 'npx playwright install chromium' manually." >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo " - ✅ Playwright and its browsers installed successfully."
|
||||||
|
PLAYWRIGHT_READY=true
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
# ----------------------------------------------------
|
# ----------------------------------------------------
|
||||||
# Checks for and installs 'gum' if not present. Sets GUM_CMD on success.
|
# Checks for and installs 'gum' if not present. Sets GUM_CMD on success.
|
||||||
# ----------------------------------------------------
|
# ----------------------------------------------------
|
||||||
|
@ -652,7 +762,12 @@ function show_command_help() {
|
||||||
backup)
|
backup)
|
||||||
echo "Creates a full backup (files + DB) of a WordPress site."
|
echo "Creates a full backup (files + DB) of a WordPress site."
|
||||||
echo
|
echo
|
||||||
echo "Usage: _do backup <folder>"
|
echo "Usage: _do backup <folder> [--quiet]"
|
||||||
|
echo
|
||||||
|
echo "Flags:"
|
||||||
|
echo " --exclude=<pattern> A file or folder pattern to exclude, relative to the backup folder."
|
||||||
|
echo " Can be used multiple times (e.g., --exclude=\"wp-content/uploads/\")."
|
||||||
|
echo " --quiet Suppress all informational output and print only the final backup URL."
|
||||||
;;
|
;;
|
||||||
checkpoint)
|
checkpoint)
|
||||||
echo "Manages versioned checkpoints of a WordPress installation's manifest."
|
echo "Manages versioned checkpoints of a WordPress installation's manifest."
|
||||||
|
@ -708,6 +823,19 @@ function show_command_help() {
|
||||||
echo " optimize Converts tables to InnoDB, reports large tables, and cleans transients."
|
echo " optimize Converts tables to InnoDB, reports large tables, and cleans transients."
|
||||||
echo " change-prefix Changes the database table prefix."
|
echo " change-prefix Changes the database table prefix."
|
||||||
;;
|
;;
|
||||||
|
disembark)
|
||||||
|
echo "Remotely installs the Disembark plugin, connects, and initiates a backup."
|
||||||
|
echo
|
||||||
|
echo "Usage: _do disembark <url> [--debug]"
|
||||||
|
echo
|
||||||
|
echo "Arguments:"
|
||||||
|
echo " <url> (Required) The full URL to the WordPress site."
|
||||||
|
echo
|
||||||
|
echo "Flags:"
|
||||||
|
echo " --debug Runs the browser automation in headed mode (not headless) for debugging."
|
||||||
|
echo
|
||||||
|
echo "You will be interactively prompted for an administrator username and password."
|
||||||
|
;;
|
||||||
dump)
|
dump)
|
||||||
echo "Dumps the content of files matching a pattern into a single text file."
|
echo "Dumps the content of files matching a pattern into a single text file."
|
||||||
echo
|
echo
|
||||||
|
@ -738,7 +866,7 @@ function show_command_help() {
|
||||||
echo
|
echo
|
||||||
echo "Subcommands:"
|
echo "Subcommands:"
|
||||||
echo " recent-files [days] Finds files modified within the last <days>. Defaults to 1 day."
|
echo " recent-files [days] Finds files modified within the last <days>. Defaults to 1 day."
|
||||||
echo " slow-plugins Identifies plugins that may be slowing down WP-CLI."
|
echo " slow-plugins [path] Identifies plugins slowing down WP-CLI. Optionally check a specific page path (e.g., \"/contact\")."
|
||||||
echo " hidden-plugins Detects active plugins that may be hidden from the standard list."
|
echo " hidden-plugins Detects active plugins that may be hidden from the standard list."
|
||||||
echo " malware Scans for malware and verifies core/plugin file integrity."
|
echo " malware Scans for malware and verifies core/plugin file integrity."
|
||||||
echo " php-tags [dir] Finds outdated PHP short tags ('<?'). Defaults to 'wp-content/'."
|
echo " php-tags [dir] Finds outdated PHP short tags ('<?'). Defaults to 'wp-content/'."
|
||||||
|
@ -925,6 +1053,7 @@ function show_usage() {
|
||||||
echo " convert-to-webp Finds and converts large images (JPG, PNG) to WebP format."
|
echo " convert-to-webp Finds and converts large images (JPG, PNG) to WebP format."
|
||||||
echo " cron Manages cron jobs and schedules tasks to run at specific times."
|
echo " cron Manages cron jobs and schedules tasks to run at specific times."
|
||||||
echo " db Performs various database operations (backup, check-autoload, optimize)."
|
echo " db Performs various database operations (backup, check-autoload, optimize)."
|
||||||
|
echo " disembark Remotely connects to a site to generate a backup."
|
||||||
echo " dump Dumps the content of files matching a pattern into a single text file."
|
echo " dump Dumps the content of files matching a pattern into a single text file."
|
||||||
echo " email Sends an email using wp_mail via WP-CLI."
|
echo " email Sends an email using wp_mail via WP-CLI."
|
||||||
echo " find Finds files, slow plugins, hidden plugins or outdated PHP tags."
|
echo " find Finds files, slow plugins, hidden plugins or outdated PHP tags."
|
||||||
|
@ -991,10 +1120,14 @@ function main() {
|
||||||
local path_flag=""
|
local path_flag=""
|
||||||
local all_files_flag=""
|
local all_files_flag=""
|
||||||
local force_flag=""
|
local force_flag=""
|
||||||
|
local format_flag=""
|
||||||
local domain_flag=""
|
local domain_flag=""
|
||||||
local output_flag=""
|
local output_flag=""
|
||||||
local exclude_patterns=()
|
local exclude_patterns=()
|
||||||
|
local backup_exclude_patterns=()
|
||||||
local positional_args=()
|
local positional_args=()
|
||||||
|
local quiet_flag=""
|
||||||
|
local debug_flag=""
|
||||||
|
|
||||||
while [[ $# -gt 0 ]]; do
|
while [[ $# -gt 0 ]]; do
|
||||||
case $1 in
|
case $1 in
|
||||||
|
@ -1042,6 +1175,10 @@ function main() {
|
||||||
force_flag=true
|
force_flag=true
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
|
--format=*)
|
||||||
|
format_flag="${1#*=}"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
--output)
|
--output)
|
||||||
output_flag=true
|
output_flag=true
|
||||||
shift
|
shift
|
||||||
|
@ -1059,6 +1196,18 @@ function main() {
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
|
--exclude=*)
|
||||||
|
backup_exclude_patterns+=("${1#*=}")
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--quiet)
|
||||||
|
quiet_flag=true
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--debug)
|
||||||
|
debug_flag=true
|
||||||
|
shift
|
||||||
|
;;
|
||||||
-*)
|
-*)
|
||||||
# This will catch unknown flags like --foo
|
# This will catch unknown flags like --foo
|
||||||
echo "Error: Unknown flag: $1" >&2
|
echo "Error: Unknown flag: $1" >&2
|
||||||
|
@ -1092,7 +1241,7 @@ function main() {
|
||||||
# This routes to the correct function based on the parsed command.
|
# This routes to the correct function based on the parsed command.
|
||||||
case "$command" in
|
case "$command" in
|
||||||
backup)
|
backup)
|
||||||
full_backup "${positional_args[1]}"
|
full_backup "${positional_args[1]}" "$quiet_flag" "$format_flag" "${backup_exclude_patterns[@]}"
|
||||||
;;
|
;;
|
||||||
checkpoint)
|
checkpoint)
|
||||||
local subcommand="${positional_args[1]}"
|
local subcommand="${positional_args[1]}"
|
||||||
|
@ -1165,6 +1314,10 @@ function main() {
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
;;
|
;;
|
||||||
|
convert-to-webp)
|
||||||
|
# Pass the optional folder (positional_args[1]) and the --all flag
|
||||||
|
convert_to_webp "${positional_args[1]}" "$all_files_flag"
|
||||||
|
;;
|
||||||
db)
|
db)
|
||||||
local arg1="${positional_args[1]}"
|
local arg1="${positional_args[1]}"
|
||||||
case "$arg1" in
|
case "$arg1" in
|
||||||
|
@ -1186,9 +1339,8 @@ function main() {
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
;;
|
;;
|
||||||
convert-to-webp)
|
disembark)
|
||||||
# Pass the optional folder (positional_args[1]) and the --all flag
|
run_disembark "${positional_args[1]}" "$debug_flag"
|
||||||
convert_to_webp "${positional_args[1]}" "$all_files_flag"
|
|
||||||
;;
|
;;
|
||||||
dump)
|
dump)
|
||||||
# There should be exactly 2 positional args total: 'dump' and the pattern.
|
# There should be exactly 2 positional args total: 'dump' and the pattern.
|
||||||
|
@ -1210,7 +1362,7 @@ function main() {
|
||||||
find_recent_files "${positional_args[2]}"
|
find_recent_files "${positional_args[2]}"
|
||||||
;;
|
;;
|
||||||
slow-plugins)
|
slow-plugins)
|
||||||
find_slow_plugins
|
find_slow_plugins "${positional_args[2]}"
|
||||||
;;
|
;;
|
||||||
hidden-plugins)
|
hidden-plugins)
|
||||||
find_hidden_plugins
|
find_hidden_plugins
|
||||||
|
@ -1401,36 +1553,66 @@ function main() {
|
||||||
# Creates a full backup of a WordPress site (files + database).
|
# Creates a full backup of a WordPress site (files + database).
|
||||||
# ----------------------------------------------------
|
# ----------------------------------------------------
|
||||||
function full_backup() {
|
function full_backup() {
|
||||||
|
# --- 1. Argument Parsing ---
|
||||||
local target_folder="$1"
|
local target_folder="$1"
|
||||||
if [ -z "$target_folder" ]; then echo "Error: Please provide a folder path." >&2; echo "Usage: _do backup <folder>" >&2; return 1; fi
|
local quiet_flag="$2"
|
||||||
|
local format_flag="$3"
|
||||||
|
shift 3 # Move past the first two arguments
|
||||||
|
local exclude_patterns=("$@") # The rest of the arguments are the exclude patterns
|
||||||
|
|
||||||
|
# --- 2. Validation ---
|
||||||
|
if [ -z "$target_folder" ]
|
||||||
|
then echo "Error: Please provide a folder path." >&2; echo "Usage: _do backup <folder>" >&2; return 1;
|
||||||
|
fi
|
||||||
if ! command -v realpath &> /dev/null; then echo "Error: 'realpath' command not found. Please install it." >&2; return 1; fi
|
if ! command -v realpath &> /dev/null; then echo "Error: 'realpath' command not found. Please install it." >&2; return 1; fi
|
||||||
if [ ! -d "$target_folder" ]; then echo "Error: Folder '$target_folder' not found." >&2; return 1; fi
|
if [ ! -d "$target_folder" ]; then echo "Error: Folder '$target_folder' not found." >&2; return 1; fi
|
||||||
|
|
||||||
|
# --- 3. Setup Paths and Filenames ---
|
||||||
# Resolve the absolute path to handle cases like "."
|
# Resolve the absolute path to handle cases like "."
|
||||||
local full_target_path; full_target_path=$(realpath "$target_folder")
|
local full_target_path; full_target_path=$(realpath "$target_folder")
|
||||||
local parent_dir; parent_dir=$(dirname "$full_target_path")
|
local parent_dir; parent_dir=$(dirname "$full_target_path")
|
||||||
local site_dir_name; site_dir_name=$(basename "$full_target_path")
|
local site_dir_name; site_dir_name=$(basename "$full_target_path")
|
||||||
|
|
||||||
local today; today=$(date +"%Y-%m-%d"); local random; random=$(head /dev/urandom | tr -dc 'a-zA-Z0-9' | head -c 7); local backup_filename="${today}_${random}.zip"; local original_dir;
|
local today; today=$(date +"%Y-%m-%d")
|
||||||
original_dir=$(pwd)
|
local random; random=$(openssl rand -hex 4 | head -c 7)
|
||||||
|
local backup_filename="${today}_${random}.zip"
|
||||||
|
local original_dir; original_dir=$(pwd)
|
||||||
|
|
||||||
# Change to the parent directory for consistent relative paths in the zip
|
# Change to the parent directory for consistent relative paths in the zip
|
||||||
cd "$parent_dir" || return 1
|
cd "$parent_dir" || return 1
|
||||||
|
|
||||||
|
# --- 4. Database Export ---
|
||||||
if ! setup_wp_cli; then echo "Error: wp-cli is not installed." >&2; cd "$original_dir"; return 1; fi
|
if ! setup_wp_cli; then echo "Error: wp-cli is not installed." >&2; cd "$original_dir"; return 1; fi
|
||||||
local home_url; home_url=$("$WP_CLI_CMD" option get home --path="$site_dir_name" --skip-plugins --skip-themes); local name; name=$("$WP_CLI_CMD" option get blogname --path="$site_dir_name" --skip-plugins --skip-themes);
|
local home_url; home_url=$("$WP_CLI_CMD" option get home --path="$site_dir_name" --skip-plugins --skip-themes)
|
||||||
|
local name; name=$("$WP_CLI_CMD" option get blogname --path="$site_dir_name" --skip-plugins --skip-themes)
|
||||||
local database_file="db_export.sql"
|
local database_file="db_export.sql"
|
||||||
|
|
||||||
|
if [[ "$quiet_flag" != "true" ]]; then
|
||||||
echo "Exporting database for '$name'...";
|
echo "Exporting database for '$name'...";
|
||||||
if ! "$WP_CLI_CMD" db export "$site_dir_name/$database_file" --path="$site_dir_name" --add-drop-table --default-character-set=utf8mb4; then
|
fi
|
||||||
|
if ! "$WP_CLI_CMD" db export "$site_dir_name/$database_file" --path="$site_dir_name" --add-drop-table --default-character-set=utf8mb4 > /dev/null; then
|
||||||
echo "Error: Database export failed." >&2
|
echo "Error: Database export failed." >&2
|
||||||
cd "$original_dir"
|
cd "$original_dir"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
if [[ "$quiet_flag" != "true" ]]; then
|
||||||
echo "Creating zip archive...";
|
echo "Creating zip archive...";
|
||||||
|
if [ ${#exclude_patterns[@]} -gt 0 ]; then
|
||||||
|
echo "Excluding the following patterns:"
|
||||||
|
for pattern in "${exclude_patterns[@]}"; do
|
||||||
|
echo " - $pattern"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- 5. Zip Archive Creation ---
|
||||||
|
local zip_exclude_args=()
|
||||||
|
zip_exclude_args+=(-x "$site_dir_name/wp-content/updraft/*") # Add default exclude
|
||||||
|
for pattern in "${exclude_patterns[@]}"; do
|
||||||
|
zip_exclude_args+=(-x "$site_dir_name/$pattern*") # Add user-defined excludes
|
||||||
|
done
|
||||||
# Create the zip in the parent directory, zipping the site directory
|
# Create the zip in the parent directory, zipping the site directory
|
||||||
if ! zip -r "$backup_filename" "$site_dir_name" -x "$site_dir_name/wp-content/updraft/*" > /dev/null; then
|
if ! zip -r "$backup_filename" "$site_dir_name" "${zip_exclude_args[@]}" > /dev/null; then
|
||||||
echo "Error: Failed to zip files." >&2
|
echo "Error: Failed to zip files." >&2
|
||||||
rm -f "$site_dir_name/$database_file"
|
rm -f "$site_dir_name/$database_file"
|
||||||
cd "$original_dir"
|
cd "$original_dir"
|
||||||
|
@ -1442,24 +1624,36 @@ function full_backup() {
|
||||||
zip "$backup_filename" "$site_dir_name/wp-config.php" > /dev/null
|
zip "$backup_filename" "$site_dir_name/wp-config.php" > /dev/null
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Cleanup and Final Steps
|
# --- 6. Cleanup and Final Report ---
|
||||||
local size; size=$(ls -lh "$backup_filename" | awk '{print $5}')
|
local size
|
||||||
|
size=$(ls -lh "$backup_filename" | awk '{print $5}')
|
||||||
rm -f "$site_dir_name/$database_file"
|
rm -f "$site_dir_name/$database_file"
|
||||||
mv "$backup_filename" "$site_dir_name/"
|
mv "$backup_filename" "$site_dir_name/"
|
||||||
|
|
||||||
local final_backup_location="$site_dir_name/$backup_filename"
|
local final_backup_location="$site_dir_name/$backup_filename"
|
||||||
|
|
||||||
cd "$original_dir"
|
cd "$original_dir"
|
||||||
|
local final_backup_location="$full_target_path/$backup_filename"
|
||||||
|
local final_url="${home_url}/${backup_filename}"
|
||||||
|
|
||||||
echo "-----------------------------------------------------";
|
if [[ "$format_flag" == "filename" ]]; then
|
||||||
echo "✅ Full site backup complete!";
|
echo "$backup_filename"
|
||||||
echo " Name: $name";
|
return 0
|
||||||
echo " Location: $final_backup_location";
|
fi
|
||||||
echo " Size: $size";
|
if [[ "$quiet_flag" == "true" ]]; then
|
||||||
echo " URL: ${home_url}/${backup_filename}";
|
echo "$final_url"
|
||||||
echo "-----------------------------------------------------";
|
return 0
|
||||||
echo "When done, remember to remove the backup file.";
|
fi
|
||||||
echo "rm -f \"$full_target_path/$backup_filename\""
|
|
||||||
|
echo "-----------------------------------------------------"
|
||||||
|
echo "✅ Full site backup complete!"
|
||||||
|
echo " Name: $name"
|
||||||
|
echo " Location: $final_backup_location"
|
||||||
|
echo " Size: $size"
|
||||||
|
echo " URL: $final_url"
|
||||||
|
echo "-----------------------------------------------------"
|
||||||
|
echo "When done, remember to remove the backup file."
|
||||||
|
echo "rm -f \"$final_backup_location\""
|
||||||
}
|
}
|
||||||
# ----------------------------------------------------
|
# ----------------------------------------------------
|
||||||
# Checkpoint Commands
|
# Checkpoint Commands
|
||||||
|
@ -3216,6 +3410,381 @@ function db_change_prefix() {
|
||||||
echo " The backup file from before the change is still available at: $db_file"
|
echo " The backup file from before the change is still available at: $db_file"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# ----------------------------------------------------
|
||||||
|
# Remotely installs the Disembark plugin, connects, and initiates a backup using an embedded Playwright script.
|
||||||
|
# ----------------------------------------------------
|
||||||
|
function run_disembark() {
|
||||||
|
local target_url_input="$1"
|
||||||
|
local debug_flag="$2"
|
||||||
|
|
||||||
|
echo "🚀 Starting Disembark process for ${target_url_input}..."
|
||||||
|
|
||||||
|
# --- 1. Pre-flight Checks for disembark-cli ---
|
||||||
|
if [ -z "$target_url_input" ];then
|
||||||
|
echo "❌ Error: Missing required URL argument." >&2
|
||||||
|
show_command_help "disembark"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! setup_disembark; then return 1; fi
|
||||||
|
|
||||||
|
# --- 2. Smart URL Parsing ---
|
||||||
|
local base_url
|
||||||
|
local login_path
|
||||||
|
|
||||||
|
# Check if the input URL contains /wp-admin or /wp-login.php
|
||||||
|
if [[ "$target_url_input" == *"/wp-admin"* || "$target_url_input" == *"/wp-login.php"* ]]; then
|
||||||
|
# If it's a backend URL, extract the base and the path
|
||||||
|
base_url=$(echo "$target_url_input" | sed -E 's#(https?://[^/]+).*#\1#')
|
||||||
|
login_path=$(echo "$target_url_input" | sed -E "s#$base_url##")
|
||||||
|
echo " - Detected backend URL. Base: '$base_url', Path: '$login_path'"
|
||||||
|
# Handle URLs with a path component that don't end in a slash (potential custom login)
|
||||||
|
elif [[ "$target_url_input" == *"/"* && "${target_url_input: -1}" != "/" ]]; then
|
||||||
|
local path_part
|
||||||
|
# Use sed -n with 'p' to ensure it only outputs on a successful match
|
||||||
|
path_part=$(echo "$target_url_input" | sed -n -E 's#https?://[^/]+(/.*)#\1#p')
|
||||||
|
|
||||||
|
# If path_part is empty, it means there was no path after the domain (e.g., https://example.com)
|
||||||
|
if [ -z "$path_part" ]; then
|
||||||
|
base_url="${target_url_input%/}"
|
||||||
|
login_path="/wp-login.php"
|
||||||
|
echo " - Homepage URL detected. Assuming default login path: '$login_path'"
|
||||||
|
# Check for deep links inside WordPress content directories
|
||||||
|
elif [[ "$path_part" == *"/wp-content"* || "$path_part" == *"/wp-includes"* ]]; then
|
||||||
|
base_url="$target_url_input"
|
||||||
|
login_path="/wp-login.php"
|
||||||
|
echo " - Deep link detected. Assuming default login for base URL."
|
||||||
|
# Otherwise, assume it's a custom login path
|
||||||
|
else
|
||||||
|
base_url=$(echo "$target_url_input" | sed -E 's#(https?://[^/]+).*#\1#')
|
||||||
|
login_path="$path_part"
|
||||||
|
echo " - Custom login path detected. Base: '$base_url', Path: '$login_path'"
|
||||||
|
fi
|
||||||
|
# Handle homepage URLs that might end with a slash
|
||||||
|
else
|
||||||
|
base_url="${target_url_input%/}" # Remove trailing slash if present
|
||||||
|
login_path="/wp-login.php"
|
||||||
|
echo " - Homepage URL detected. Assuming default login path: '$login_path'"
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
# --- 3. Attempt backup with a potentially stored token ---
|
||||||
|
echo "✅ Attempting backup using a stored token..."
|
||||||
|
if "$DISEMBARK_CMD" backup "$base_url"; then
|
||||||
|
echo "✨ Backup successful using a pre-existing token."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- 4. If backup fails, proceed to full browser authentication ---
|
||||||
|
echo "⚠️ Backup with stored token failed. A new connection token is likely required."
|
||||||
|
echo "Proceeding with browser authentication..."
|
||||||
|
|
||||||
|
# --- Pre-flight Checks for browser automation ---
|
||||||
|
if ! setup_playwright; then return 1; fi
|
||||||
|
if ! setup_gum; then return 1; fi
|
||||||
|
|
||||||
|
# --- Define the Playwright script using a Heredoc ---
|
||||||
|
local PLAYWRIGHT_SCRIPT
|
||||||
|
PLAYWRIGHT_SCRIPT=$(cat <<'EOF'
|
||||||
|
// --- Embedded Playwright Script ---
|
||||||
|
const { chromium } = require('playwright');
|
||||||
|
const https = require('https');
|
||||||
|
const fs = require('fs');
|
||||||
|
const os = require('os');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
const PLUGIN_ZIP_URL = 'https://github.com/DisembarkHost/disembark-connector/releases/latest/download/disembark-connector.zip';
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
const [, , baseUrl, loginPath, username, password, debugFlag] = process.argv;
|
||||||
|
|
||||||
|
if (!baseUrl || !loginPath || !username || !password) {
|
||||||
|
console.error('Usage: node disembark-browser.js <baseUrl> <loginPath> <username> <password> [debug]');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const loginUrl = baseUrl + loginPath;
|
||||||
|
const adminUrl = baseUrl + '/wp-admin/';
|
||||||
|
|
||||||
|
const isHeadless = debugFlag !== 'true';
|
||||||
|
const browser = await chromium.launch({ headless: isHeadless });
|
||||||
|
|
||||||
|
const context = await browser.newContext({
|
||||||
|
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36'
|
||||||
|
});
|
||||||
|
const page = await context.newPage();
|
||||||
|
try {
|
||||||
|
// 1. LOGIN
|
||||||
|
process.stdout.write(` - Step 1/5: Authenticating with WordPress at ${loginUrl}...`);
|
||||||
|
await page.goto(loginUrl, { waitUntil: 'domcontentloaded' });
|
||||||
|
|
||||||
|
if (!(await page.isVisible('#user_login'))) {
|
||||||
|
console.log(' Failed.');
|
||||||
|
console.error('LOGIN_URL_INVALID');
|
||||||
|
process.exit(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
await page.fill('#user_login', username);
|
||||||
|
await page.fill('#user_pass', password);
|
||||||
|
await page.click('#wp-submit');
|
||||||
|
|
||||||
|
try {
|
||||||
|
await page.waitForSelector('#wpadminbar, #login_error, #correct-admin-email', { timeout: 60000 });
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error('Authentication timed out. The page did not load the admin bar, a login error, or the admin email confirmation screen.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (await page.isVisible('#login_error')) {
|
||||||
|
const errorText = await page.locator('#login_error').textContent();
|
||||||
|
console.error(`LOGIN_FAILED: ${errorText.trim()}`);
|
||||||
|
process.exit(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (await page.isVisible('#correct-admin-email')) {
|
||||||
|
process.stdout.write(' Admin email confirmation required. Submitting...');
|
||||||
|
await page.click('#correct-admin-email');
|
||||||
|
await page.waitForSelector('#wpadminbar', { timeout: 60000 });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(await page.isVisible('#wpadminbar'))) {
|
||||||
|
throw new Error('Authentication failed. Admin bar not found after login.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Recovery Step: Navigate to the main dashboard to bypass any welcome/update screens ---
|
||||||
|
if (!page.url().startsWith(adminUrl)) {
|
||||||
|
process.stdout.write(' Navigating to main dashboard to bypass intermediate pages...');
|
||||||
|
await page.goto(adminUrl, { waitUntil: 'networkidle' });
|
||||||
|
await page.waitForSelector('#wpadminbar'); // Re-confirm we are in the admin area
|
||||||
|
process.stdout.write(' Done.');
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(' Success!');
|
||||||
|
|
||||||
|
// 2. CHECK IF PLUGIN EXISTS & ACTIVATE IF NEEDED
|
||||||
|
process.stdout.write(' - Step 2/5: Checking plugin status...');
|
||||||
|
await page.goto(`${adminUrl}plugins.php`, { waitUntil: 'networkidle' });
|
||||||
|
|
||||||
|
const pluginRow = page.locator('tr[data-slug="disembark-connector"]');
|
||||||
|
if (await pluginRow.count() > 0) {
|
||||||
|
console.log(' Plugin found.');
|
||||||
|
const activateLink = pluginRow.locator('a.edit:has-text("Activate")');
|
||||||
|
if (await activateLink.count() > 0) {
|
||||||
|
process.stdout.write(' - Activating existing plugin...');
|
||||||
|
await Promise.all([
|
||||||
|
page.waitForNavigation({ waitUntil: 'networkidle' }),
|
||||||
|
activateLink.click()
|
||||||
|
]);
|
||||||
|
console.log(' Activated!');
|
||||||
|
} else {
|
||||||
|
console.log(' - Plugin already active.');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 3. UPLOAD AND INSTALL PLUGIN
|
||||||
|
console.log(' Plugin not found, proceeding with installation.');
|
||||||
|
process.stdout.write(' - Step 3/5: Downloading plugin...');
|
||||||
|
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'disembark-'));
|
||||||
|
const pluginZipPath = path.join(tempDir, 'disembark-connector.zip');
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
const file = fs.createWriteStream(pluginZipPath);
|
||||||
|
const request = (url) => {
|
||||||
|
https.get(url, (response) => {
|
||||||
|
if (response.statusCode > 300 && response.statusCode < 400 && response.headers.location) {
|
||||||
|
request(response.headers.location);
|
||||||
|
} else {
|
||||||
|
response.pipe(file);
|
||||||
|
file.on('finish', () => file.close(resolve));
|
||||||
|
}
|
||||||
|
}).on('error', (err) => {
|
||||||
|
fs.unlinkSync(pluginZipPath);
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
request(PLUGIN_ZIP_URL);
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(' Download complete.');
|
||||||
|
process.stdout.write(' - Uploading and installing...');
|
||||||
|
await page.goto(`${adminUrl}plugin-install.php?tab=upload`);
|
||||||
|
await page.setInputFiles('input#pluginzip', pluginZipPath);
|
||||||
|
await page.waitForSelector('input#install-plugin-submit:not([disabled])', { timeout: 10000 });
|
||||||
|
|
||||||
|
await page.click('input#install-plugin-submit');
|
||||||
|
|
||||||
|
const activationLinkSelector = 'a:has-text("Activate Plugin"), a.activate-now, .button.activate-now';
|
||||||
|
const alreadyInstalledSelector = 'body:has-text("Destination folder already exists.")';
|
||||||
|
const mixedSuccessSelector = 'body:has-text("Plugin installed successfully.")';
|
||||||
|
const genericErrorSelector = '.wrap > .error, .wrap > #message.error';
|
||||||
|
|
||||||
|
try {
|
||||||
|
await page.waitForSelector(
|
||||||
|
`${activationLinkSelector}, ${alreadyInstalledSelector}, ${mixedSuccessSelector}, ${genericErrorSelector}`,
|
||||||
|
{ timeout: 90000 }
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error('Timed out waiting for a response after clicking "Install Now".');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (await page.locator(activationLinkSelector).count() > 0) {
|
||||||
|
const activateButton = page.locator(activationLinkSelector);
|
||||||
|
await Promise.all([
|
||||||
|
page.waitForNavigation({ waitUntil: 'networkidle' }),
|
||||||
|
activateButton.first().click(),
|
||||||
|
]);
|
||||||
|
console.log(' Installed & Activated!');
|
||||||
|
} else if (await page.locator(alreadyInstalledSelector).count() > 0) {
|
||||||
|
console.log(' Plugin already installed.');
|
||||||
|
} else if (await page.locator(mixedSuccessSelector).count() > 0) {
|
||||||
|
console.log(' Install succeeded, but page reported an error. Navigating to plugins page to activate...');
|
||||||
|
await page.goto(`${adminUrl}plugins.php`, { waitUntil: 'networkidle' });
|
||||||
|
const pluginRow = page.locator('tr[data-slug="disembark-connector"]');
|
||||||
|
const activateLink = pluginRow.locator('a.edit:has-text("Activate")');
|
||||||
|
if (await activateLink.count() > 0) {
|
||||||
|
await Promise.all([
|
||||||
|
page.waitForNavigation({ waitUntil: 'networkidle' }),
|
||||||
|
activateLink.click()
|
||||||
|
]);
|
||||||
|
console.log(' Activated!');
|
||||||
|
} else {
|
||||||
|
console.log(' - Plugin was already active on the plugins page.');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const errorText = await page.locator(genericErrorSelector).first().textContent();
|
||||||
|
throw new Error(`Plugin installation failed with a WordPress error: ${errorText.trim()}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.unlinkSync(pluginZipPath);
|
||||||
|
fs.rmdirSync(tempDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. RETRIEVE TOKEN
|
||||||
|
process.stdout.write(' - Step 4/5: Retrieving connection token...');
|
||||||
|
const tokenPageUrl = `${adminUrl}plugin-install.php?tab=plugin-information&plugin=disembark-connector`;
|
||||||
|
await page.goto(tokenPageUrl, { waitUntil: 'networkidle' });
|
||||||
|
|
||||||
|
const tokenElement = page.locator('div#section-description > code');
|
||||||
|
if (await tokenElement.count() === 0) {
|
||||||
|
throw new Error('Could not find the connection token element on the page.');
|
||||||
|
}
|
||||||
|
const token = await tokenElement.first().textContent();
|
||||||
|
console.log(' Token found!');
|
||||||
|
// 5. OUTPUT TOKEN FOR BASH SCRIPT
|
||||||
|
process.stdout.write(' - Step 5/5: Sending token back to script...');
|
||||||
|
console.log(token.trim());
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`\nError: ${error.message}`);
|
||||||
|
process.exit(1);
|
||||||
|
} finally {
|
||||||
|
await browser.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
||||||
|
EOF
|
||||||
|
)
|
||||||
|
# --- Helper function for getting credentials ---
|
||||||
|
_get_credentials() {
|
||||||
|
echo "Please provide WordPress administrator credentials:"
|
||||||
|
username=$("$GUM_CMD" input --placeholder="Enter WordPress username...")
|
||||||
|
if [ -z "$username" ]; then
|
||||||
|
echo "No username provided. Aborting." >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
password=$("$GUM_CMD" input --placeholder="Enter WordPress password..." --password)
|
||||||
|
if [ -z "$password" ]; then
|
||||||
|
echo "No password provided. Aborting." >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Helper for running playwright ---
|
||||||
|
run_playwright_and_get_token() {
|
||||||
|
local url_to_run="$1"
|
||||||
|
local path_to_run="$2"
|
||||||
|
local user_to_run="$3"
|
||||||
|
local pass_to_run="$4"
|
||||||
|
|
||||||
|
echo "🤖 Launching browser to automate login and plugin setup..."
|
||||||
|
echo " - Attempting login at: ${url_to_run}${path_to_run}"
|
||||||
|
|
||||||
|
set -o pipefail
|
||||||
|
local full_output
|
||||||
|
full_output=$(echo "$PLAYWRIGHT_SCRIPT" | node - "$url_to_run" "$path_to_run" "$user_to_run" "$pass_to_run" "$debug_flag" | tee /dev/tty)
|
||||||
|
local exit_code=$?
|
||||||
|
set +o pipefail
|
||||||
|
|
||||||
|
if [ $exit_code -eq 3 ]; then
|
||||||
|
return 3 # Bad credentials
|
||||||
|
elif [ $exit_code -eq 2 ]; then
|
||||||
|
return 2 # Invalid URL
|
||||||
|
elif [ $exit_code -ne 0 ]; then
|
||||||
|
return 1 # General failure
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$full_output" | tail -n 1 | tr -d '[:space:]'
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Authentication Loop ---
|
||||||
|
local token
|
||||||
|
local playwright_exit_code
|
||||||
|
|
||||||
|
# Initial credential prompt
|
||||||
|
if ! _get_credentials; then return 1; fi
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
token=$(run_playwright_and_get_token "$base_url" "$login_path" "$username" "$password")
|
||||||
|
playwright_exit_code=$?
|
||||||
|
|
||||||
|
if [ $playwright_exit_code -eq 0 ]; then
|
||||||
|
break # Success
|
||||||
|
elif [ $playwright_exit_code -eq 2 ]; then # Invalid URL
|
||||||
|
echo "⚠️ The login URL '${base_url}${login_path}' appears to be incorrect."
|
||||||
|
local new_login_url
|
||||||
|
new_login_url=$("$GUM_CMD" input --placeholder="Enter the full, correct WordPress Admin URL...")
|
||||||
|
|
||||||
|
if [ -z "$new_login_url" ]; then
|
||||||
|
echo "No URL provided. Aborting." >&2; return 1;
|
||||||
|
fi
|
||||||
|
base_url=$(echo "$new_login_url" | sed -E 's#(https?://[^/]+).*#\1#')
|
||||||
|
login_path=$(echo "$new_login_url" | sed -E "s#$base_url##")
|
||||||
|
continue # Retry loop with new URL
|
||||||
|
elif [ $playwright_exit_code -eq 3 ]; then # Bad credentials
|
||||||
|
echo "⚠️ Login failed. The credentials may be incorrect."
|
||||||
|
if "$GUM_CMD" confirm "Re-enter credentials and try again?"; then
|
||||||
|
if ! _get_credentials; then return 1; fi
|
||||||
|
continue # Retry loop with new credentials
|
||||||
|
else
|
||||||
|
echo "Authentication cancelled." >&2; return 1;
|
||||||
|
fi
|
||||||
|
else # General failure
|
||||||
|
echo "❌ Browser automation failed. Please check errors above." >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# --- Final Check and Backup ---
|
||||||
|
if [ $playwright_exit_code -ne 0 ] || [ -z "$token" ]; then
|
||||||
|
echo "❌ Could not retrieve a token. Aborting." >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "✅ Browser automation successful. Token retrieved."
|
||||||
|
echo "📞 Connecting and starting backup with disembark-cli..."
|
||||||
|
if ! "$DISEMBARK_CMD" connect "${base_url}" "${token}"; then
|
||||||
|
echo "❌ Error: Failed to connect using the retrieved token." >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! "$DISEMBARK_CMD" backup "${base_url}"; then
|
||||||
|
echo "❌ Error: Backup command failed after connecting." >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "✨ Disembark process complete!"
|
||||||
|
}
|
||||||
# ----------------------------------------------------
|
# ----------------------------------------------------
|
||||||
# Dumps the content of files matching a pattern into a single text file.
|
# Dumps the content of files matching a pattern into a single text file.
|
||||||
# ----------------------------------------------------
|
# ----------------------------------------------------
|
||||||
|
@ -3424,19 +3993,52 @@ function find_recent_files() {
|
||||||
# Identifies plugins that may be slowing down WP-CLI command execution.
|
# Identifies plugins that may be slowing down WP-CLI command execution.
|
||||||
# ----------------------------------------------------
|
# ----------------------------------------------------
|
||||||
function find_slow_plugins() {
|
function find_slow_plugins() {
|
||||||
_get_wp_execution_time() { local output; output=$("$WP_CLI_CMD" "$@" --debug 2>&1); echo "$output" | perl -ne '/Debug \(bootstrap\): Running command: .+\(([^s]+s)/ && print $1'; }
|
local page_to_check="${1}"
|
||||||
|
|
||||||
|
_get_wp_execution_time() {
|
||||||
|
local output
|
||||||
|
output=$("$WP_CLI_CMD" "$@" --debug 2>&1)
|
||||||
|
echo "$output" | perl -ne '/Debug \(bootstrap\): Running command: .+\(([^s]+s)/ && print $1'
|
||||||
|
}
|
||||||
if ! setup_wp_cli; then echo "❌ Error: WP-CLI (wp command) not found." >&2; return 1; fi
|
if ! setup_wp_cli; then echo "❌ Error: WP-CLI (wp command) not found." >&2; return 1; fi
|
||||||
if ! "$WP_CLI_CMD" core is-installed --quiet; then echo "❌ Error: This does not appear to be a WordPress installation." >&2; return 1; fi
|
if ! "$WP_CLI_CMD" core is-installed --quiet; then echo "❌ Error: This does not appear to be a WordPress installation." >&2; return 1; fi
|
||||||
|
|
||||||
echo "🚀 WordPress Plugin Performance Test 🚀"
|
local base_command_args=("plugin" "list")
|
||||||
echo "This script measures the execution time of 'wp plugin list --debug' under various conditions."
|
local skip_argument="--skip-plugins"
|
||||||
echo ""
|
local description="wp plugin list --debug"
|
||||||
echo "📋 Initial Baseline Measurements for 'wp plugin list --debug':"
|
|
||||||
|
|
||||||
local time_no_theme_s; printf " ⏳ Measuring time with NO themes loaded (--skip-themes)... "; time_no_theme_s=$(_get_wp_execution_time plugin list --skip-themes); echo "Time: $time_no_theme_s"
|
if [ -n "$page_to_check" ]; then
|
||||||
local time_no_plugins_s; printf " ⏳ Measuring time with NO plugins loaded (--skip-plugins)... "; time_no_plugins_s=$(_get_wp_execution_time plugin list --skip-plugins); echo "Time: $time_no_plugins_s"
|
echo "🚀 WordPress Plugin Performance Test for page: '$page_to_check' 🚀"
|
||||||
local base_time_s; printf " ⏳ Measuring base time (ALL plugins & theme active)... "; base_time_s=$(_get_wp_execution_time plugin list)
|
description="wp render \"$page_to_check\" --debug"
|
||||||
if [[ -z "$base_time_s" ]]; then echo "❌ Error: Could not measure base execution time." >&2; return 1; fi;
|
|
||||||
|
# Check for wp render and install if missing
|
||||||
|
if ! "$WP_CLI_CMD" help render &> /dev/null; then
|
||||||
|
echo "ℹ️ 'wp render' command not found. Installing 'render-command' plugin..."
|
||||||
|
if ! "$WP_CLI_CMD" plugin install https://github.com/austinginder/render-command/releases/latest/download/render-command.zip --activate --force; then
|
||||||
|
echo "❌ Error: Failed to install 'render-command' plugin. Aborting." >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
echo "✅ 'render-command' plugin installed successfully."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Update command variables to use 'wp render'
|
||||||
|
base_command_args=("render" "$page_to_check")
|
||||||
|
skip_argument="--without-plugin"
|
||||||
|
else
|
||||||
|
echo "🚀 WordPress Plugin Performance Test 🚀"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "This script measures the execution time of '$description' under various conditions."
|
||||||
|
echo ""
|
||||||
|
echo "📋 Initial Baseline Measurements for '$description':"
|
||||||
|
|
||||||
|
local base_time_s
|
||||||
|
printf " ⏳ Measuring base time (ALL plugins & theme active)... "
|
||||||
|
base_time_s=$(_get_wp_execution_time "${base_command_args[@]}")
|
||||||
|
if [[ -z "$base_time_s" ]]; then
|
||||||
|
echo "❌ Error: Could not measure base execution time." >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
echo "Base time: $base_time_s"
|
echo "Base time: $base_time_s"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
|
@ -3445,21 +4047,37 @@ function find_slow_plugins() {
|
||||||
active_plugins+=("$line")
|
active_plugins+=("$line")
|
||||||
done < <("$WP_CLI_CMD" plugin list --field=name --status=active)
|
done < <("$WP_CLI_CMD" plugin list --field=name --status=active)
|
||||||
|
|
||||||
if [[ ${#active_plugins[@]} -eq 0 ]]; then echo "ℹ️ No active plugins found to test."; return 0; fi
|
if [[ ${#active_plugins[@]} -eq 0 ]]; then
|
||||||
|
echo "ℹ️ No active plugins found to test."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
echo "📊 Measuring impact of individual plugins (compared to '${base_time_s}' base time):"
|
echo "📊 Measuring impact of individual plugins (compared to '${base_time_s}' base time):"
|
||||||
echo "A larger positive 'Impact' suggests the plugin contributes more to the load time of this specific WP-CLI command."
|
echo "A larger positive 'Impact' suggests the plugin contributes more to the load time of this specific WP-CLI command."
|
||||||
echo "---------------------------------------------------------------------------------"; printf "%-40s | %-15s | %-15s\n" "Plugin Skipped" "Time w/ Skip" "Impact (Base-Skip)"; echo "---------------------------------------------------------------------------------"
|
echo "---------------------------------------------------------------------------------"
|
||||||
local results=(); for plugin in "${active_plugins[@]}"; do
|
printf "%-40s | %-15s | %-15s\n" "Plugin Skipped" "Time w/ Skip" "Impact (Base-Skip)"
|
||||||
local time_with_skip_s; time_with_skip_s=$(_get_wp_execution_time plugin list --skip-plugins="$plugin")
|
echo "---------------------------------------------------------------------------------"
|
||||||
|
local results=()
|
||||||
|
for plugin in "${active_plugins[@]}"; do
|
||||||
|
# Skip the render-command plugin itself when using the render method
|
||||||
|
if [[ -n "$page_to_check" && "$plugin" == "render-command" ]]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
local time_with_skip_s
|
||||||
|
time_with_skip_s=$(_get_wp_execution_time "${base_command_args[@]}" "${skip_argument}=$plugin")
|
||||||
|
|
||||||
if [[ -n "$time_with_skip_s" ]]; then
|
if [[ -n "$time_with_skip_s" ]]; then
|
||||||
local diff_s; diff_s=$(awk -v base="${base_time_s%s}" -v skip="${time_with_skip_s%s}" 'BEGIN { printf "%.3f", base - skip }')
|
local diff_s
|
||||||
|
diff_s=$(awk -v base="${base_time_s%s}" -v skip="${time_with_skip_s%s}" 'BEGIN { printf "%.3f", base - skip }')
|
||||||
local impact_sign=""
|
local impact_sign=""
|
||||||
if [[ $(awk -v diff="$diff_s" 'BEGIN { print (diff > 0) }') -eq 1 ]]; then
|
if [[ $(awk -v diff="$diff_s" 'BEGIN { print (diff > 0) }') -eq 1 ]]; then
|
||||||
impact_sign="+"
|
impact_sign="+"
|
||||||
fi
|
fi
|
||||||
results+=("$(printf "%.3f" "$diff_s")|$plugin|$time_with_skip_s|${impact_sign}${diff_s}s")
|
results+=("$(printf "%.3f" "$diff_s")|$plugin|$time_with_skip_s|${impact_sign}${diff_s}s")
|
||||||
else results+=("0.000|$plugin|Error|Error measuring"); fi
|
else
|
||||||
|
results+=("0.000|$plugin|Error|Error measuring")
|
||||||
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
local sorted_results=()
|
local sorted_results=()
|
||||||
|
@ -3468,14 +4086,19 @@ function find_slow_plugins() {
|
||||||
done < <(printf "%s\n" "${results[@]}" | sort -t'|' -k1,1nr)
|
done < <(printf "%s\n" "${results[@]}" | sort -t'|' -k1,1nr)
|
||||||
|
|
||||||
for result_line in "${sorted_results[@]}"; do
|
for result_line in "${sorted_results[@]}"; do
|
||||||
local p_name; p_name=$(echo "$result_line" | cut -d'|' -f2); local t_skip; t_skip=$(echo "$result_line" | cut -d'|' -f3); local i_str; i_str=$(echo "$result_line" | cut -d'|' -f4)
|
local p_name
|
||||||
|
p_name=$(echo "$result_line" | cut -d'|' -f2)
|
||||||
|
local t_skip
|
||||||
|
t_skip=$(echo "$result_line" | cut -d'|' -f3)
|
||||||
|
local i_str
|
||||||
|
i_str=$(echo "$result_line" | cut -d'|' -f4)
|
||||||
printf "%-40s | %-15s | %-15s\n" "$p_name" "$t_skip" "$i_str"
|
printf "%-40s | %-15s | %-15s\n" "$p_name" "$t_skip" "$i_str"
|
||||||
done
|
done
|
||||||
echo "---------------------------------------------------------------------------------";
|
echo "---------------------------------------------------------------------------------"
|
||||||
echo "";
|
echo ""
|
||||||
echo "✅ Test Complete"
|
echo "✅ Test Complete"
|
||||||
echo "💡 Note: This measures impact on a specific WP-CLI command. For front-end or";
|
echo "💡 Note: This measures impact on a specific WP-CLI command. For front-end or"
|
||||||
echo " admin profiling, consider using a plugin like Query Monitor or New Relic.";
|
echo " admin profiling, consider using a plugin like Query Monitor or New Relic."
|
||||||
echo ""
|
echo ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3503,6 +4126,7 @@ function find_hidden_plugins() {
|
||||||
local raw_count
|
local raw_count
|
||||||
raw_count=$(echo "$active_plugins_raw" | wc -l | xargs)
|
raw_count=$(echo "$active_plugins_raw" | wc -l | xargs)
|
||||||
|
|
||||||
|
|
||||||
# Compare the counts of the two lists.
|
# Compare the counts of the two lists.
|
||||||
if [[ "$regular_count" == "$raw_count" ]]; then
|
if [[ "$regular_count" == "$raw_count" ]]; then
|
||||||
echo "✅ No hidden plugins detected. The standard and raw plugin lists match ($regular_count plugins)."
|
echo "✅ No hidden plugins detected. The standard and raw plugin lists match ($regular_count plugins)."
|
||||||
|
@ -3961,7 +4585,7 @@ function migrate_site() {
|
||||||
local backup_url="$1"
|
local backup_url="$1"
|
||||||
local update_urls_flag="$2"
|
local update_urls_flag="$2"
|
||||||
|
|
||||||
echo "🚀 Starting Site Migration 🚀"
|
echo "🚀 Starting Site Migration ..."
|
||||||
|
|
||||||
# --- Pre-flight Checks ---
|
# --- Pre-flight Checks ---
|
||||||
if ! setup_wp_cli; then echo "❌ Error: WP-CLI not found." >&2; return 1; fi
|
if ! setup_wp_cli; then echo "❌ Error: WP-CLI not found." >&2; return 1; fi
|
||||||
|
@ -3991,6 +4615,9 @@ function migrate_site() {
|
||||||
cd "$restore_dir" || return 1
|
cd "$restore_dir" || return 1
|
||||||
|
|
||||||
local local_file_name; local_file_name=$(basename "$backup_url")
|
local local_file_name; local_file_name=$(basename "$backup_url")
|
||||||
|
if [ -f "${home_directory}/${local_file_name}" ]; then
|
||||||
|
mv "${home_directory}/${local_file_name}" "${private_dir}/${local_file_name}"
|
||||||
|
fi
|
||||||
|
|
||||||
# Handle special URLs
|
# Handle special URLs
|
||||||
if [[ "$backup_url" == *"admin-ajax.php"* ]]; then
|
if [[ "$backup_url" == *"admin-ajax.php"* ]]; then
|
||||||
|
@ -4109,14 +4736,44 @@ function migrate_site() {
|
||||||
cd "$home_directory"
|
cd "$home_directory"
|
||||||
|
|
||||||
# --- Database Migration ---
|
# --- Database Migration ---
|
||||||
local database; database=$(find "$restore_dir" -type f -name '*.sql' -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" ")
|
local database
|
||||||
|
if [[ "$(uname)" == "Darwin" ]]; then
|
||||||
|
# macOS/BSD version using stat -f
|
||||||
|
database=$(find "$restore_dir" "$home_directory" -type f -name '*.sql' -print0 | xargs -0 stat -f '%m %N' | sort -n | tail -1 | cut -f2- -d" ")
|
||||||
|
else
|
||||||
|
# Linux version using find -printf
|
||||||
|
database=$(find "$restore_dir" "$home_directory" -type f -name '*.sql' -printf '%T@ %p\n' | sort -n | tail -1 | cut -d' ' -f2-)
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ -z "$database" || ! -f "$database" ]]; then
|
if [[ -z "$database" || ! -f "$database" ]]; then
|
||||||
echo "⚠️ Warning: No .sql file found in backup. Skipping database import.";
|
echo "⚠️ Warning: No .sql file found in backup. Skipping database import.";
|
||||||
else
|
else
|
||||||
echo "Importing database from $database..."
|
echo "Importing database from $database..."
|
||||||
local search_privacy; search_privacy=$( "$WP_CLI_CMD" option get blog_public --skip-plugins --skip-themes )
|
local search_privacy; search_privacy=$( "$WP_CLI_CMD" option get blog_public --skip-plugins --skip-themes )
|
||||||
|
|
||||||
|
# Outputs table prefix and updates if different
|
||||||
|
cd "${restore_dir}/${wordpresspath}/../"
|
||||||
|
if [ -f wp-config.php ]; then
|
||||||
|
table_prefix=$( cat wp-config.php | grep table_prefix | perl -n -e '/\047(.+)\047/&& print $1' )
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd "$home_directory"
|
||||||
|
current_table_prefix=$( wp config get table_prefix --skip-plugins --skip-themes )
|
||||||
|
if [[ $table_prefix != "" && $table_prefix != "$current_table_prefix" ]]; then
|
||||||
|
echo "Updating table prefix from $current_table_prefix to $table_prefix"
|
||||||
|
wp config set table_prefix $table_prefix --skip-plugins --skip-themes
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Reset the database first
|
||||||
"$WP_CLI_CMD" db reset --yes --skip-plugins --skip-themes
|
"$WP_CLI_CMD" db reset --yes --skip-plugins --skip-themes
|
||||||
"$WP_CLI_CMD" db import "$database" --skip-plugins --skip-themes
|
|
||||||
|
# Import using WP-CLI
|
||||||
|
if ! "$WP_CLI_CMD" db import "$database"; then
|
||||||
|
echo " Error: Database import failed. Please check the error message above." >&2
|
||||||
|
cd "$home_directory"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
"$WP_CLI_CMD" cache flush --skip-plugins --skip-themes
|
"$WP_CLI_CMD" cache flush --skip-plugins --skip-themes
|
||||||
"$WP_CLI_CMD" option update blog_public "$search_privacy" --skip-plugins --skip-themes
|
"$WP_CLI_CMD" option update blog_public "$search_privacy" --skip-plugins --skip-themes
|
||||||
|
|
||||||
|
@ -4145,7 +4802,7 @@ function migrate_site() {
|
||||||
echo "$alter_queries" | "$WP_CLI_CMD" db query --skip-plugins --skip-themes
|
echo "$alter_queries" | "$WP_CLI_CMD" db query --skip-plugins --skip-themes
|
||||||
fi
|
fi
|
||||||
|
|
||||||
"$WP_CLI_CMD" rewrite flush --skip-plugins --skip-themes
|
"$WP_CLI_CMD" rewrite flush
|
||||||
if "$WP_CLI_CMD" plugin is-active woocommerce --skip-plugins --skip-themes &>/dev/null; then
|
if "$WP_CLI_CMD" plugin is-active woocommerce --skip-plugins --skip-themes &>/dev/null; then
|
||||||
"$WP_CLI_CMD" wc tool run regenerate_product_attributes_lookup_table --user=1 --skip-plugins --skip-themes
|
"$WP_CLI_CMD" wc tool run regenerate_product_attributes_lookup_table --user=1 --skip-plugins --skip-themes
|
||||||
fi
|
fi
|
||||||
|
|
176
changelog.md
176
changelog.md
|
@ -1,135 +1,113 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
An analysis of the two script versions reveals significant enhancements in version 1.2, focusing on new features, improved dependency management, and increased command organization.
|
## [1.4] - 2025-09-19
|
||||||
|
|
||||||
|
### 🚀 New Features
|
||||||
|
|
||||||
|
* **Automated Remote Site Snapshots `disembark`:** A powerful new command that uses browser automation (Playwright) to remotely log into a WordPress site, install and activate the Disembark Connector plugin, retrieve a connection token, and initiate a backup. It's a complete, hands-off solution for snapshotting a remote site without prior access.
|
||||||
|
* **Debug Mode for Browser Automation `--debug`:** The `disembark` command now includes a `--debug` flag to run the browser automation in a visible (headed) mode, making it easy to troubleshoot login or installation issues.
|
||||||
|
|
||||||
|
### ✨ Improvements
|
||||||
|
|
||||||
|
* **Selective Backups `backup --exclude`:** The `backup` command now accepts multiple `--exclude="<pattern>"` flags, allowing you to easily omit specific files or directories (like large cache folders) from the backup archive.
|
||||||
|
* **Script-Friendly Backups `backup --quiet`:** Added a `--quiet` flag to the `backup` command to suppress all informational output and print only the final backup URL, perfect for scripting and automation.
|
||||||
|
* **Contextual Performance Testing `find slow-plugins`:** The `find slow-plugins` command can now test performance against a specific front-end page render (e.g., `_do find slow-plugins /about-us`) instead of just the WP-CLI bootstrap, providing more relevant and accurate results for front-end slowdowns.
|
||||||
|
|
||||||
## [1.3] - 2025-08-16
|
## [1.3] - 2025-08-16
|
||||||
|
|
||||||
### Added
|
### 🚀 New Features
|
||||||
|
|
||||||
* **New `zip` Command:** A utility to create a zip archive of any specified folder. If the command is run within a WordPress installation, it also provides a public URL for the generated archive, making it easy to share.
|
* **Quick Archiving `zip`:** A new utility to create a zip archive of any specified folder. When run within a WordPress site, it provides a public URL for the generated archive, making it easy to share.
|
||||||
* **New `clean plugins` Subcommand:** This new subcommand finds and deletes all inactive plugins. It is designed to work safely on both single-site and multisite WordPress installations, ensuring it only removes plugins that are not active on any site in a network.
|
* **Plugin Cleanup `clean plugins`:** A new subcommand to find and delete all inactive plugins. It works safely on both single-site and multisite installations, only removing plugins that are not active anywhere in a network.
|
||||||
|
|
||||||
### Changed
|
### ✨ Improvements
|
||||||
|
|
||||||
* **`convert-to-webp` Command Overhaul:** The image conversion utility has been significantly enhanced:
|
* **⚡️ High-Speed Image Conversion `convert-to-webp`:** The image conversion utility has been massively overhauled for performance and flexibility.
|
||||||
* It now accepts an optional folder path, allowing users to convert images in directories other than the default `wp-content/uploads`.
|
* It now accepts an optional folder path to convert images outside the default `wp-content/uploads` directory.
|
||||||
* Performance has been dramatically improved by processing multiple images in parallel, significantly reducing the time required for large batches of images.
|
* Performance is dramatically improved by processing multiple images in parallel.
|
||||||
* The script now provides clearer feedback to the user by indicating which image detection method (`identify` or a PHP fallback) is being used.
|
* The script now provides clearer feedback by indicating which image detection method (`identify` or a PHP fallback) is being used.
|
||||||
* **`dump` Command Naming:** The output file from the `dump` command is now more intelligently named based on the source directory (e.g., `my-plugin.txt`), rather than a generic filename.
|
* **Smarter Naming `dump`:** The output file from the `dump` command is now intelligently named based on the source directory (e.g., `my-plugin-dump.txt`) for better organization.
|
||||||
|
|
||||||
### Fixed
|
### 🐛 Bug Fixes
|
||||||
|
|
||||||
* **Image Conversion Reliability:** The `convert-to-webp` command is now more robust. It verifies that an image file exists before attempting to process it, which prevents potential errors when encountering broken file paths.
|
* **Robust Image Handling `convert-to-webp`:** The command is now more reliable, as it verifies that an image file exists before attempting to process it, preventing errors from broken file paths.
|
||||||
|
|
||||||
## [1.2] - 2025-07-10
|
## [1.2] - 2025-07-10
|
||||||
|
|
||||||
### Added
|
### 🚀 New Features
|
||||||
|
|
||||||
* **New `vault` Command Suite:** A comprehensive set of commands for managing secure, remote, full-site snapshots using Restic and Backblaze B2. This includes creating, listing, deleting, and Browse snapshots, as well as mounting the entire backup repository.
|
* **🏦 Secure Snapshot Vault `vault`:** A comprehensive suite of commands for managing secure, remote, full-site snapshots using Restic and Backblaze B2. Includes creating, listing, deleting, and browsing snapshots, as well as mounting the entire backup repository.
|
||||||
* **New `find` Command Suite:** Centralizes several diagnostic tools under a single command:
|
* **🔍 Advanced Diagnostics `find`:** Centralizes several diagnostic tools under a single powerful command.
|
||||||
* `find recent-files`: Finds files modified within a specified number of days.
|
* `find recent-files`: Finds recently modified files.
|
||||||
* `find slow-plugins`: Relocated from a top-level command to identify plugins that may be slowing down WP-CLI.
|
* `find slow-plugins`: Identifies plugins slowing down WP-CLI.
|
||||||
* `find hidden-plugins`: Detects active plugins that may be hidden from the standard list.
|
* `find hidden-plugins`: Detects active plugins hidden from the admin list.
|
||||||
* `find malware`: Scans for suspicious code patterns and verifies core and plugin file integrity.
|
* `find malware`: Scans for suspicious code and verifies file integrity.
|
||||||
* `find php-tags`: Scans for outdated PHP short tags.
|
* `find php-tags`: Scans for outdated PHP short tags.
|
||||||
* **New `install` Command Suite:** Simplifies the installation of helper and premium plugins:
|
* **🔌 Easy Plugin Installation `install`:** Simplifies installing common helper and premium plugins.
|
||||||
* `install kinsta-mu`: Installs the Kinsta Must-Use plugin.
|
* `install kinsta-mu`: Installs the Kinsta Must-Use plugin.
|
||||||
* `install helper`: Installs the CaptainCore Helper plugin.
|
* `install helper`: Installs the CaptainCore Helper plugin.
|
||||||
* `install events-calendar-pro`: Installs The Events Calendar Pro after prompting for a license.
|
* `install events-calendar-pro`: Installs The Events Calendar Pro after prompting for a license.
|
||||||
* **New Standalone Commands:**
|
* **New Standalone Utilities:**
|
||||||
* `https` Applies HTTPS to all site URLs with an interactive prompt for 'www' preference.
|
* `https`: Applies HTTPS to all site URLs with an interactive prompt for `www` preference.
|
||||||
* `launch`: A dedicated command to launch a site, updating the URL from a development to a live domain.
|
* `launch`: A dedicated command to launch a site, updating the URL from a dev to a live domain.
|
||||||
* `upgrade`: Allows the `_do` script to upgrade itself to the latest version.
|
* `upgrade`: Allows the `_do` script to upgrade itself to the latest version.
|
||||||
* `email`: Provides an interactive prompt to send an email using `wp_mail` via WP-CLI.
|
* `email`: Provides an interactive prompt to send an email using `wp_mail` via WP-CLI.
|
||||||
* **New `db` Subcommand:**
|
* **⚙️ Database Prefix Changer `db change-prefix`:** Safely changes the database table prefix after automatically creating a backup.
|
||||||
* `db change-prefix`: Safely changes the database table prefix after creating a backup.
|
|
||||||
* **Advanced Dependency Sideloading:**
|
### ✨ Improvements
|
||||||
* Introduced new `setup_restic`, `setup_imagemagick`, and `setup_git` functions to automatically download and use these tools in a private directory if they are not installed system-wide.
|
|
||||||
* The ImageMagick setup cleverly extracts the `identify` binary from an AppImage to avoid system-level installation and FUSE issues.
|
* **New Command Flags:** Added several flags for non-interactive use and more control:
|
||||||
* **New Flags:**
|
* `--all` for `convert-to-webp` to convert all images regardless of size.
|
||||||
* `--all` for `convert-to-webp` to convert all images regardless of their file size.
|
* `--domain` for `launch` to specify the new domain for scripting.
|
||||||
* `--domain` for `launch` to specify the new domain non-interactively.
|
|
||||||
* `--force` for `install kinsta-mu` to bypass the Kinsta environment check.
|
* `--force` for `install kinsta-mu` to bypass the Kinsta environment check.
|
||||||
* `--output` for `vault snapshots` to provide a non-interactive list.
|
* `--output` for `vault snapshots` to provide a non-interactive list.
|
||||||
|
* **🛠️ Advanced Dependency Sideloading:** `_do` can now automatically download and use tools like `restic`, `git`, and `imagemagick` in a private directory if they aren't installed system-wide. The ImageMagick setup cleverly extracts the `identify` binary from an AppImage to avoid system installation and FUSE issues.
|
||||||
|
* **Reliable WebP Detection:** The `convert-to-webp` command no longer requires a system-installed `identify` command. It now uses the automatically sideloaded ImageMagick binary and includes a PHP fallback to reliably check if an image is already a WebP file.
|
||||||
|
* **Command Organization:** Refactored several commands into logical groups for better usability (e.g., `reset-wp` is now `reset wp`, `slow-plugins` is now `find slow-plugins`).
|
||||||
|
* **Private Directory Discovery:** The script is now much smarter at finding a writable private directory, checking WP-CLI configs, WPEngine paths, and common parent structures (`../private`) before falling back to the home directory.
|
||||||
|
* **Public Dump URL:** The `dump` command now generates a public URL for the created text file if run within a WordPress site.
|
||||||
|
|
||||||
### Changed
|
### 🐛 Bug Fixes
|
||||||
|
|
||||||
* **Command Refactoring:**
|
* **Cross-Platform PHP Tag Scanning `php-tags`:** Rewrote the command to use a more portable `grep` syntax, ensuring it works correctly on systems like macOS where the `-P` flag is not available by default.
|
||||||
* The `reset-wp` command has been moved to `reset wp`. It now features a more user-friendly interactive selector for choosing the admin user, rather than requiring a command-line flag.
|
|
||||||
* The `reset-permissions` command has been moved to `reset permissions`.
|
|
||||||
* The `slow-plugins` command has been moved to `find slow-plugins`.
|
|
||||||
* **Dependency Management Overhaul:** The `setup_*` functions (e.g., `setup_gum`, `setup_cwebp`, `setup_rclone`) have been completely rewritten. They are now more robust, checking for existing local binaries, using `curl` for downloads, and intelligently finding the executable within archives before extraction.
|
|
||||||
* **Private Directory Discovery:** The `_get_private_dir` helper function is now significantly more intelligent. It checks WP-CLI configurations, WPEngine-specific paths, and common parent directory structures (`../private`, `../tmp`) before falling back to the user's home directory.
|
|
||||||
* **WebP Conversion:** The `convert-to-webp` command no longer relies on a system-installed `identify` command. It now uses the sideloaded ImageMagick binary and includes a PHP-based fallback (`_is_webp_php`) to check if a file is already in WebP format.
|
|
||||||
* **Dump Command URL:** The `dump` command will now generate and display a public URL for the created text file if the script is run within a WordPress installation.
|
|
||||||
* **Database Autoload Check:** The query in `db check-autoload` was updated to check for `autoload IN ('yes', 'on')` to be compatible with more database configurations.
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
* **PHP Tag Scanning Portability:** The `php-tags` command was rewritten to use a more portable `grep` syntax, removing the dependency on the `-P` (Perl-compatible regex) flag. This ensures the command works correctly on systems like macOS where this flag is not available by default.
|
|
||||||
|
|
||||||
## [1.1] - 2025-06-21
|
## [1.1] - 2025-06-21
|
||||||
|
|
||||||
### Features
|
### 🚀 New Features
|
||||||
|
|
||||||
- **Checkpoint Management (`checkpoint`)**: A comprehensive suite of commands has been added to create, list, show, and revert WordPress installation checkpoints. This feature allows users to capture the state of their site's core, themes, and plugins and revert to a previous state if needed.
|
* **💾 Checkpoint Management `checkpoint`:** A full suite to create, list, show, and revert WordPress installation checkpoints. Capture the state of your site's core, themes, and plugins and easily roll back changes.
|
||||||
- `checkpoint create`: Creates a new checkpoint.
|
* **🔄 Integrated Update Management `update`:** Streamlines the WordPress update process. `update all` automatically creates 'before' and 'after' checkpoints around the updates, logging all changes for easy review.
|
||||||
- `checkpoint list`: Interactively lists all available checkpoints for inspection.
|
* **⏰ Cron Job Management `cron`:** Schedule any `_do` command to run at specific intervals. Includes commands to `enable`, `list`, `add`, `delete`, and `run` scheduled tasks.
|
||||||
- `checkpoint list-generate`: Generates a detailed, cached list of checkpoints for faster access.
|
* **🩺 WP-CLI Health Checker `wpcli`:** A new utility to diagnose issues with WP-CLI itself by identifying themes or plugins that cause warnings.
|
||||||
- `checkpoint revert [<hash>]`: Reverts the site's files to a specified checkpoint.
|
* **💥 Site Reset `reset-wp`:** A powerful command to reset a WordPress installation to its default state, reinstalling core and the latest default theme.
|
||||||
- `checkpoint show <hash>`: Shows the changes within a specific checkpoint, including file and manifest diffs.
|
* **🧹 Advanced Cleaning `clean`:**
|
||||||
- `checkpoint latest`: Displays the hash of the most recent checkpoint.
|
* `clean themes`: Deletes all inactive themes while preserving the latest default WordPress theme.
|
||||||
- **Update Management (`update`)**: This new command set streamlines the WordPress update process by integrating with the checkpoint system.
|
* `clean disk`: Launches an interactive disk usage analyzer to find and manage large files.
|
||||||
- `update all`: Automatically creates a 'before' checkpoint, runs all core, plugin, and theme updates, creates an 'after' checkpoint, and logs the changes.
|
* **⚙️ Database Optimization `db optimize`:** A new command to convert tables to InnoDB, clean transients, and report the largest tables.
|
||||||
- `update list`: Shows a list of past update events, allowing users to inspect the changes between the 'before' and 'after' states.
|
|
||||||
- `update list-generate`: Generates a detailed, cached list of all update events.
|
|
||||||
- **Cron Job Management (`cron`)**: Enables the scheduling of `_do` commands.
|
|
||||||
- `cron enable`: Configures a system cron job to run scheduled `_do` tasks.
|
|
||||||
- `cron list`: Lists all scheduled commands.
|
|
||||||
- `cron add`: Adds a new command to the schedule with a specified start time and frequency.
|
|
||||||
- `cron delete`: Removes a scheduled command by its ID.
|
|
||||||
- `cron run`: Executes any due commands.
|
|
||||||
- **WP-CLI Health Checker (`wpcli`)**: A new utility to diagnose issues with WP-CLI itself.
|
|
||||||
- `wpcli check`: Helps identify themes or plugins that are causing warnings or errors in WP-CLI.
|
|
||||||
- **Site Reset (`reset-wp`)**: A command to reset a WordPress installation to its default state. It reinstalls the latest WordPress core, removes all plugins and themes, and installs the latest default theme.
|
|
||||||
- **PHP Tag Checker (`php-tags`)**: Scans a directory for outdated PHP short tags (`<?`) that can cause issues on modern servers.
|
|
||||||
- **Inactive Theme Cleaner (`clean themes`)**: Deletes all inactive themes, but intelligently preserves the latest default WordPress theme as a fallback.
|
|
||||||
- **Interactive Disk Cleaner (`clean disk`)**: Launches an interactive disk usage analyzer to help find and manage large files and directories.
|
|
||||||
- **Database Optimization (`db optimize`)**: A new command to optimize the WordPress database by converting tables to InnoDB, cleaning transients, and reporting the largest tables.
|
|
||||||
|
|
||||||
### Improvements
|
### ✨ Improvements
|
||||||
|
|
||||||
- **Command Structure & Aliases**: The script has been refactored for better organization. Commands like `backup-db` and `db-check-autoload` are now subcommands under the `db` command (`db backup`, `db check-autoload`).
|
* **Command Structure:** Refactored commands for better organization. `backup-db` is now `db backup`, and `monitor` is now a subcommand with specific targets (`traffic`, `errors`, etc.).
|
||||||
- **Dependency Management**:
|
* **🛠️ Automated Dependency Handling:** The script now automatically checks for, downloads, and installs missing dependencies like `gum`, `cwebp`, and `rclone` into a `~/private` directory.
|
||||||
- The script now automatically checks for, downloads, and installs missing dependencies like `gum`, `cwebp`, and `rclone` into a `~/private` directory to avoid system-wide installations.
|
* **🏗️ Development Workflow:** Added `compile.sh` and `watch.sh` scripts to streamline development by combining source files into a single distributable script automatically.
|
||||||
- `git` is now a required dependency for the new `checkpoint` and `update` features.
|
* **Flexible Migrations `migrate`:** Added an `--update-urls` flag to control URL replacement and improved temporary file handling.
|
||||||
- **Compilation and Development**:
|
* **File Exclusion `dump`:** The `dump` command was enhanced with an `-x` flag to exclude specific files or directories.
|
||||||
- `compile.sh`: A new compilation script is introduced to combine the main script and individual command files from the `commands` directory into a single distributable file (`_do.sh`).
|
|
||||||
- `watch.sh`: A watcher script has been added to automatically re-compile the script when changes are detected in the source files, streamlining development.
|
|
||||||
- **Monitor Command**: The `monitor` command is now a subcommand with more specific targets: `traffic`, `errors`, `access.log`, and `error.log`.
|
|
||||||
- **Suspend Command**: The `suspend` command now uses a more specific filename (`do-suspend.php`) for the mu-plugin to avoid conflicts.
|
|
||||||
- **Migration (`migrate`)**:
|
|
||||||
- The `migrate` command now has a `--update-urls` flag to control whether the site's URL should be updated after import.
|
|
||||||
- It now intelligently finds a private directory for storing temporary files.
|
|
||||||
- **File Dumper (`dump`)**: The `dump` command has been enhanced with an `-x` flag to allow for the exclusion of specific files or directories from the dump.
|
|
||||||
|
|
||||||
### Fixes
|
### 🐛 Bug Fixes
|
||||||
|
|
||||||
- **Migration File Handling**: The `migrate_site` function was updated to handle cases where `wp-content` exists in the backup but contains no plugins or themes to move, preventing potential errors.
|
* **Migration File Handling:** The `migrate` function now handles cases where `wp-content` exists but contains no plugins or themes, preventing errors.
|
||||||
- **Dump Command Self-Exclusion**: The `run_dump` function now explicitly excludes its own output file from the list of files to be dumped, preventing it from including itself in the generated text file.
|
* **Dump Self-Exclusion:** The `dump` command now explicitly excludes its own output file to prevent it from being included in the dump.
|
||||||
|
|
||||||
## [1.0] - 2025-06-10
|
## [1.0] - 2025-06-10
|
||||||
|
|
||||||
### Added
|
### 🎉 Initial Release
|
||||||
|
|
||||||
- **Initial Release**: The first version of the `_do` script.
|
* **Full and Database Backups `backup`, `backup-db`**: Create full-site (files + DB) and database-only backups.
|
||||||
- **Full and Database Backups (`backup`, `backup-db`)**: Utilities to create full-site (files + database) backups and database-only backups.
|
* **Real-time Traffic Monitoring `monitor`**: Watch server access logs in real-time with a summarized view of top hits.
|
||||||
- **Real-time Traffic Monitoring (`monitor`)**: A command to watch server access logs in real-time, displaying top hits in a readable format.
|
* **Performance Profiling `slow-plugins`**: Identify WordPress plugins that slow down WP-CLI commands.
|
||||||
- **Performance Profiling (`slow-plugins`)**: A tool to identify WordPress plugins that may be slowing down WP-CLI command execution.
|
* **Image Optimization `convert-to-webp`**: Find and convert large JPG/PNG images to the modern WebP format.
|
||||||
- **Image Optimization (`convert-to-webp`)**: Finds and converts large JPG and PNG images to the more efficient WebP format.
|
* **Site Migration `migrate`**: Automate migrating a WordPress site from a backup URL or local file.
|
||||||
- **Site Migration (`migrate`)**: A command to automate the migration of a WordPress site from a backup URL or local file.
|
* **File Content Dumper `dump`**: Concatenate the contents of files matching a pattern into a single text file.
|
||||||
- **File Content Dumper (`dump`)**: A utility to concatenate the contents of files matching a specific pattern into a single text file.
|
* **Database Autoload Checker `db-check-autoload`**: Check the size of autoloaded options in the database.
|
||||||
- **Database Autoload Checker (`db-check-autoload`)**: A tool to check the size and top 25 largest autoloaded options in the WordPress database.
|
* **Permission Resetter `reset-permissions`**: Reset file and folder permissions to standard defaults (755/644).
|
||||||
- **Permission Resetter (`reset-permissions`)**: A command to reset file and folder permissions to standard defaults (755 for directories, 644 for files).
|
* **Site Suspension `suspend`**: Activate or deactivate a "Website Suspended" maintenance page.
|
||||||
- **Site Suspension (`suspend`)**: A utility to activate or deactivate a "Website Suspended" message for visitors.
|
|
||||||
- **Automatic Dependency Installation**: The script automatically downloads and installs `gum` and `cwebp` if they are not found on the system.
|
|
2
main
2
main
|
@ -8,7 +8,7 @@
|
||||||
# ----------------------------------------------------
|
# ----------------------------------------------------
|
||||||
|
|
||||||
# --- Global Variables ---
|
# --- Global Variables ---
|
||||||
CAPTAINCORE_DO_VERSION="1.3"
|
CAPTAINCORE_DO_VERSION="1.4"
|
||||||
GUM_VERSION="0.14.4"
|
GUM_VERSION="0.14.4"
|
||||||
CWEBP_VERSION="1.5.0"
|
CWEBP_VERSION="1.5.0"
|
||||||
RCLONE_VERSION="1.69.3"
|
RCLONE_VERSION="1.69.3"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue