#!/bin/bash # # Script to automate WordPress installation. # Includes dependency checks, WP-CLI installation, database setup, # WordPress core installation, and configuration. # # --- Configuration --- # Exit immediately if a command exits with a non-zero status. set -e # Treat unset variables as an error when substituting. set -u # Pipe commands return the exit status of the last command in the pipe 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 --- # Avoid insecure defaults. Consider making these mandatory or generating random ones. # Leaving them blank to force user input or argument passing. WP_ADMIN_USER="" WP_ADMIN_PASS="" WP_ADMIN_EMAIL="" WP_ROOT="/var/www/webroot/ROOT" # Default WordPress root directory DOMAIN="" # Domain will be determined or required DB_HOST="127.0.0.1" DB_ROOT_USER="root" DB_ROOT_PASS="" # Require user to provide this for security SKIP_DB_ROOT_RESET="false" # By default, perform the root password reset (use --skip-db-root-reset to disable) WEB_USER="litespeed" # Web server user (e.g., www-data, apache, nginx) WEB_GROUP="litespeed" # Web server group # --- 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 "Automates the installation of WordPress.\n" printf "\n" printf "Required Options:\n" printf " --wpusername=USERNAME WordPress admin username (mandatory)\n" printf " --wppassword=PASSWORD WordPress admin password (mandatory)\n" printf " --wpemail=EMAIL WordPress admin email (mandatory)\n" printf " --dbrootpass=PASSWORD Current MySQL/MariaDB root password (mandatory unless resetting)\n" printf "\n" printf "Optional Options:\n" printf " --wproot=PATH WordPress installation directory (default: %s)\n" "$WP_ROOT" printf " --domain=DOMAIN Domain name for the site (default: auto-detected from hostname)\n" printf " --webuser=USER Web server user (default: %s)\n" "$WEB_USER" printf " --webgroup=GROUP Web server group (default: %s)\n" "$WEB_GROUP" printf " --dbhost=HOST Database host (default: %s)\n" "$DB_HOST" printf " --reset-db-root-pass Perform the risky root password reset (requires sudo without password)\n" printf " -h, --help Display this help message\n" printf "\n" printf "Example:\n" printf " %s --wpusername=myuser --wppassword='securePass' --wpemail=me@example.com --dbrootpass='currentRootPass'\n" "$0" printf " %s --wpusername=myuser --wppassword='securePass' --wpemail=me@example.com --reset-db-root-pass --domain=example.com\n" "$0" exit 1 } # Function to check if a command exists command_exists() { command -v "$1" &> /dev/null } # Function to generate a random secure password generate_password() { openssl rand -base64 16 # Increased length slightly } # Function to clean up temporary files cleanup() { info "Cleaning up temporary files..." rm -f "$WP_CLI_CONFIG_PATH" # Add any other cleanup tasks here } # --- Argument Parsing --- # Using getopt for better argument handling TEMP=$(getopt -o h --longoptions help,wpusername:,wppassword:,wpemail:,wproot:,domain:,dbhost:,dbrootpass:,reset-db-root-pass,webuser:,webgroup: -n "$0" -- "$@") if [ $? != 0 ]; then error_exit "Terminating... Invalid arguments." fi # Note the quotes around "$TEMP": they are essential! eval set -- "$TEMP" unset TEMP PERFORM_DB_ROOT_RESET="false" while true; do case "$1" in --wpusername) WP_ADMIN_USER="$2"; shift 2 ;; --wppassword) WP_ADMIN_PASS="$2"; shift 2 ;; --wpemail) WP_ADMIN_EMAIL="$2"; shift 2 ;; --wproot) WP_ROOT="$2"; shift 2 ;; --domain) DOMAIN="$2"; shift 2 ;; --dbhost) DB_HOST="$2"; shift 2 ;; --dbrootpass) DB_ROOT_PASS="$2"; shift 2 ;; --reset-db-root-pass) PERFORM_DB_ROOT_RESET="true"; shift 1 ;; --webuser) WEB_USER="$2"; shift 2 ;; --webgroup) WEB_GROUP="$2"; shift 2 ;; -h|--help) usage ;; --) shift ; break ;; # End of options *) error_exit "Internal error! Unexpected option: $1";; esac done # --- Validation --- info "Validating parameters..." if [[ -z "$WP_ADMIN_USER" ]]; then error_exit "WordPress admin username (--wpusername) is required."; fi if [[ -z "$WP_ADMIN_PASS" ]]; then error_exit "WordPress admin password (--wppassword) is required."; fi if [[ -z "$WP_ADMIN_EMAIL" ]]; then error_exit "WordPress admin email (--wpemail) is required."; fi if [[ ! "$WP_ADMIN_EMAIL" =~ ^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$ ]]; then error_exit "Invalid email format for --wpemail."; fi if [[ "$PERFORM_DB_ROOT_RESET" == "false" && -z "$DB_ROOT_PASS" ]]; then error_exit "Database root password (--dbrootpass) is required unless --reset-db-root-pass is used."; fi if [[ "$PERFORM_DB_ROOT_RESET" == "true" && -n "$DB_ROOT_PASS" ]]; then warning "Both --reset-db-root-pass and --dbrootpass provided. Will perform reset and ignore provided root password."; fi if [[ ! -d "$WP_ROOT" ]]; then error_exit "WordPress root directory '$WP_ROOT' does not exist or is not a directory."; fi if ! id "$WEB_USER" &>/dev/null; then error_exit "Web user '$WEB_USER' does not exist."; fi if ! getent group "$WEB_GROUP" &>/dev/null; then error_exit "Web group '$WEB_GROUP' does not exist."; fi # --- Determine Domain --- if [[ -z "$DOMAIN" ]]; then if ! command_exists hostname; then error_exit "'hostname' command not found. Please specify --domain."; fi FULL_HOSTNAME=$(hostname -f) # Attempt to remove common node prefixes, make this more robust if needed DOMAIN=$(echo "$FULL_HOSTNAME" | sed -E 's/^(node[0-9]*-|wp-|web-|host-)//') if [[ -z "$DOMAIN" || "$DOMAIN" == "$FULL_HOSTNAME" ]]; then warning "Could not reliably determine domain from hostname '$FULL_HOSTNAME'. Using it as is." DOMAIN="$FULL_HOSTNAME" fi info "Auto-detected domain: $DOMAIN (Use --domain to override)" else info "Using specified domain: $DOMAIN" fi # --- Dependency Checks --- info "Checking dependencies..." declare -a dependencies=("php" "mysql" "curl" "openssl" "sudo" "hostname" "sed" "systemctl" "getopt") for cmd in "${dependencies[@]}"; do if ! command_exists "$cmd"; then error_exit "Required command '$cmd' is not installed. Please install it first." fi done # Specific check for pkill if reset is chosen if [[ "$PERFORM_DB_ROOT_RESET" == "true" ]]; then if ! command_exists pkill; then error_exit "'pkill' command not found, but required for --reset-db-root-pass."; fi if ! command_exists mysqld_safe; then error_exit "'mysqld_safe' command not found, but required for --reset-db-root-pass."; fi fi success "All dependencies found." # --- WP-CLI Setup --- WP_CLI_PATH="/usr/local/bin/wp" # Check if WP-CLI is installed and executable if ! command_exists wp; then info "WP-CLI not found. Installing WP-CLI..." if ! curl -o wp-cli.phar https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar; then error_exit "Failed to download WP-CLI." fi chmod +x wp-cli.phar if ! sudo mv wp-cli.phar "$WP_CLI_PATH"; then error_exit "Failed to move WP-CLI to $WP_CLI_PATH. Check sudo permissions." fi # Verify installation if ! command_exists wp; then error_exit "WP-CLI installation failed unexpectedly." fi success "WP-CLI installed successfully to $WP_CLI_PATH" else success "WP-CLI is already installed." fi # Set up temporary WP-CLI config for HTTP_HOST if needed export WP_CLI_CONFIG_PATH="/tmp/wp-cli-config-$RANDOM.yml" cat > "$WP_CLI_CONFIG_PATH" < /dev/null; then error_exit "Failed to connect to database using provided root credentials. Check user, password, and host." fi success "Database root connection successful." fi # --- Create WordPress Database and User --- info "Creating WordPress database '$DB_NAME' and user '$DB_USER'..." # Use printf for safer password injection into the command SQL_COMMAND=$(printf "CREATE DATABASE IF NOT EXISTS %s CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER IF NOT EXISTS '%s'@'%s' IDENTIFIED BY '%s'; GRANT ALL PRIVILEGES ON %s.* TO '%s'@'%s'; FLUSH PRIVILEGES;" \ "$DB_NAME" "$DB_USER" "$DB_HOST" "$DB_PASSWORD" "$DB_NAME" "$DB_USER" "$DB_HOST") if ! mysql -u "$DB_ROOT_USER" -p"$DB_ROOT_PASS" -h "$DB_HOST" -e "$SQL_COMMAND"; then error_exit "Failed to create WordPress database or user. Check MySQL/MariaDB logs and permissions." fi success "Database and user created successfully." # --- WordPress Installation --- info "Navigating to WordPress root: $WP_ROOT" cd "$WP_ROOT" || error_exit "Failed to change directory to $WP_ROOT" # Backup existing wp-config.php if it exists if [[ -f "wp-config.php" ]]; then BACKUP_NAME="wp-config.php.bak.$(date +%Y%m%d%H%M%S)" info "Backing up existing wp-config.php to $BACKUP_NAME" cp wp-config.php "$BACKUP_NAME" fi # Download WordPress core files if necessary (index.php is a good indicator) # Run wp commands as the web user if possible, falling back to --allow-root if needed WP_RUN_ARGS=("--path=$WP_ROOT") # Determine if sudo is needed to run as web user SUDO_CMD="" if [[ "$(id -u)" -ne "$(id -u "$WEB_USER")" ]]; then SUDO_CMD="sudo -u $WEB_USER" # Check if we can sudo without password, otherwise need --allow-root if ! sudo -n -u "$WEB_USER" true &>/dev/null; then warning "Cannot sudo to '$WEB_USER' without a password. Using --allow-root for WP-CLI commands." SUDO_CMD="" # Clear sudo command WP_RUN_ARGS+=("--allow-root") else info "Running WP-CLI commands as user '$WEB_USER'." fi fi if [[ ! -f "index.php" || ! -d "wp-admin" ]]; then info "WordPress core files not found. Downloading..." if ! $SUDO_CMD wp core download "${WP_RUN_ARGS[@]}" --skip-content --version=latest; then error_exit "Failed to download WordPress core files." fi success "WordPress core downloaded." else info "WordPress files already exist. Skipping download." fi # --- Create wp-config.php --- # Using wp cli command is generally preferred, but manual creation is more robust if WP-CLI has issues. # We'll stick to the manual creation from your original script as it included specific fixes. info "Creating wp-config.php..." # Generate Salts using WP-CLI if possible, otherwise fallback to openssl SALTS=$($SUDO_CMD wp config salt generate --raw "${WP_RUN_ARGS[@]}" 2>/dev/null || { warning "Could not generate salts using WP-CLI, falling back to openssl (less standard format)." echo "define( 'AUTH_KEY', '$(generate_password)' );" echo "define( 'SECURE_AUTH_KEY', '$(generate_password)' );" echo "define( 'LOGGED_IN_KEY', '$(generate_password)' );" echo "define( 'NONCE_KEY', '$(generate_password)' );" echo "define( 'AUTH_SALT', '$(generate_password)' );" echo "define( 'SECURE_AUTH_SALT', '$(generate_password)' );" echo "define( 'LOGGED_IN_SALT', '$(generate_password)' );" echo "define( 'NONCE_SALT', '$(generate_password)' );" }) # Use cat with heredoc for wp-config.php creation cat > wp-config.php <