#!/bin/bash # WordPress Version Check and Update Script # Copyright 2024 A.D. Federated Computer, Inc. # saint@federated.computer # --------------------------------------- # This script checks the running WordPress version in a Docker container and updates # the IMAGE_VERSION in the .env file to match if needed. # # Version Detection Methods (in order): # 1. WP-CLI: Uses 'wp core version' # 2. PHP Direct: Uses wp_eval to get bloginfo('version') # 3. File Check: Reads version from /bitnami/wordpress/wp-includes/version.php # 4. Database Primary: Reads from wp_options._transient_wp_core_block_css_files # 5. Database Backup: Reads from wp_options._site_transient_update_core # # The script will: # - Use the highest valid version found from any method # - Check if that version is newer than the one in .env # - If newer, look for matching Docker image on Docker Hub # - If exact version not found, use next newer version or closest older version # - Update .env with the new version and add IMAGE_VERSION_HOLD=true if needed # Enable debug output ## set -x # Set your container name and .env file path CONTAINER_NAME="wordpress" APP_DIR="/federated/apps/$CONTAINER_NAME" ENV_FILE="$APP_DIR/.env" DOCKER_COMPOSE_FILE="$APP_DIR/docker-compose.yml" # Function to validate version string is_valid_version() { local version=$1 # Allow x, x.y, x.y.z, or x.y.z.a where all parts are integers [[ $version =~ ^[0-9]+(\.[0-9]+)*$ ]] || return 1 # Count parts local parts IFS='.' read -ra parts <<< "$version" # Allow 1 to 4 parts [ ${#parts[@]} -le 4 ] || return 1 return 0 } echo "Starting WordPress version check and update script..." # Function to get version from different sources get_wp_version() { local wpcli_version php_version file_version db_version admin_version highest_version # 1. Try wp-cli version wpcli_version=$(docker exec "$CONTAINER_NAME" wp core version --allow-root) || true if ! is_valid_version "$wpcli_version"; then wpcli_version=""; fi # 2. Try version.php direct access php_version=$(docker exec "$CONTAINER_NAME" wp eval 'echo get_bloginfo("version");' --allow-root) || true if ! is_valid_version "$php_version"; then php_version=""; fi # 3. Try direct file content check (fixed quotes) file_version=$(docker exec "$CONTAINER_NAME" bash -c "grep '\\\$wp_version = ' /bitnami/wordpress/wp-includes/version.php | sed -n 's/.*= .\\([0-9.]*\\).*/\\1/p'") || true if [ -z "$file_version" ]; then file_version=$(docker exec "$CONTAINER_NAME" bash -c "grep '\\\$wp_version = ' /opt/bitnami/wordpress/wp-includes/version.php | sed -n 's/.*= .\\([0-9.]*\\).*/\\1/p'") || true fi if ! is_valid_version "$file_version"; then file_version=""; fi # 4. Try database version through wp-cli with socket path db_version=$(docker exec "$CONTAINER_NAME" bash -c ' is_valid_version() { local version=$1 # Allow x, x.y, x.y.z, or x.y.z.a formats [[ $version =~ ^[0-9]+(\.[0-9]+)*$ ]] || return 1 # Count parts and ensure not more than 4 local parts IFS='.' read -ra parts <<< "$version" [ ${#parts[@]} -le 4 ] || return 1 return 0 } if [ -f /opt/bitnami/mysql/bin/mariadb ]; then MYSQL_CMD="/opt/bitnami/mysql/bin/mariadb" else MYSQL_CMD="/opt/bitnami/mysql/bin/mysql" fi export MYSQL_PWD="$WORDPRESS_DATABASE_PASSWORD" # First try _transient_wp_core_block_css_files as it has simpler structure version=$($MYSQL_CMD -u "$WORDPRESS_DATABASE_USER" \ -h "$WORDPRESS_DATABASE_HOST" \ --skip-ssl \ "$WORDPRESS_DATABASE_NAME" \ -N -e "SELECT option_value FROM wp_options WHERE option_name = \"_transient_wp_core_block_css_files\";" | \ sed -n "s/.*s:7:\"version\";s:[0-9]*:\"\([0-9][0-9.]*\)\".*/\1/p") # Validate first version before accepting it if ! is_valid_version "$version"; then # If first attempt fails or gives invalid version, try _site_transient_update_core version=$($MYSQL_CMD -u "$WORDPRESS_DATABASE_USER" \ -h "$WORDPRESS_DATABASE_HOST" \ --skip-ssl \ "$WORDPRESS_DATABASE_NAME" \ -N -e "SELECT option_value FROM wp_options WHERE option_name = \"_site_transient_update_core\";" | \ sed -n "s/.*\"version_checked\":\"\([0-9][0-9.]*\)\".*/\1/p") fi # Only output version if it is valid if is_valid_version "$version"; then echo "$version" fi ') || true if ! is_valid_version "$db_version"; then db_version=""; fi # 5. Try reading version from version.php using PHP admin_version=$(docker exec "$CONTAINER_NAME" bash -c "php -r ' \$files = array(\"/bitnami/wordpress/wp-includes/version.php\", \"/opt/bitnami/wordpress/wp-includes/version.php\"); foreach (\$files as \$file) { if (file_exists(\$file)) { include \$file; echo \$wp_version; break; } } '") || true if ! is_valid_version "$admin_version"; then admin_version=""; fi # Log all found versions (to stderr for debugging) { echo "WP-CLI reports version: ${wpcli_version:-not found}" >&2 echo "wp eval reports version: ${php_version:-not found}" >&2 echo "File check reports version: ${file_version:-not found}" >&2 echo "Database reports version: ${db_version:-not found}" >&2 echo "Admin page reports version: ${admin_version:-not found}" >&2 } # Compare all versions and take the highest one, excluding db version local versions=() [ ! -z "$wpcli_version" ] && versions+=("$wpcli_version") [ ! -z "$php_version" ] && versions+=("$php_version") [ ! -z "$file_version" ] && versions+=("$file_version") [ ! -z "$admin_version" ] && versions+=("$admin_version") if [ ${#versions[@]} -eq 0 ]; then echo "Error: Could not determine WordPress version from any source" >&2 exit 1 fi # Return just the version number printf '%s\n' "${versions[@]}" | sort -V | tail -n 1 } # Function to find closest available version on Docker Hub find_closest_version() { local target_version=$1 local available_versions="" local page=1 local page_size=100 local next_url="https://hub.docker.com/v2/repositories/bitnami/wordpress/tags?page_size=$page_size" echo "Retrieving available WordPress versions from Docker Hub..." >&2 # Keep fetching pages until we hit an empty page or no next URL while [ ! -z "$next_url" ]; do echo "Fetching page $page..." >&2 # Get the current page and extract versions and next URL local response=$(curl -s "$next_url") # Extract versions from this page local page_versions=$(echo "$response" | \ grep -o '"name":"[^"]*"' | \ grep -o '[0-9]\+\.[0-9]\+\.[0-9]\+' | \ sort -V | uniq) # If we got no versions, break if [ -z "$page_versions" ]; then break fi # Append to our list of versions available_versions="$available_versions"$'\n'"$page_versions" # Get next page URL and decode \u0026 to & next_url=$(echo "$response" | \ grep -o '"next":"[^"]*"' | \ cut -d'"' -f4 | \ sed 's/\\u0026/\&/g' ) # If no next URL or we've hit page limit, break if [ -z "$next_url" ] || [ $page -ge 10 ]; then break fi ((page++)) done # Clean up and sort the final list available_versions=$(echo "$available_versions" | grep -v '^$' | sort -V | uniq) if [ -z "$available_versions" ]; then echo "Failed to get versions from Docker Hub" >&2 return 1 fi # Print all versions for debugging echo "Available versions:" >&2 echo "$available_versions" >&2 # First, check if target version exists if echo "$available_versions" | grep -Fxq "$target_version"; then printf '%s\n' "$target_version" return 0 fi # If not, find next newer version local newer_version newer_version=$(echo "$available_versions" | awk -v ver="$target_version" '$1 > ver' | head -n1) # If no newer version, find closest older version if [ -z "$newer_version" ]; then echo "$available_versions" | awk -v ver="$target_version" '$1 <= ver' | tail -n1 else printf '%s\n' "$newer_version" fi } # Function to compare version numbers version_greater_than() { local ver1=$1 local ver2=$2 if ! is_valid_version "$ver1" || ! is_valid_version "$ver2"; then echo "Invalid version format" >&2 return 1 fi # Convert versions to arrays local v1_parts=() v2_parts=() IFS='.' read -ra v1_parts <<< "$ver1" IFS='.' read -ra v2_parts <<< "$ver2" # Pad shorter version with zeros while [ ${#v1_parts[@]} -lt 4 ]; do v1_parts+=("0") done while [ ${#v2_parts[@]} -lt 4 ]; do v2_parts+=("0") done # Compare each part numerically for i in {0..3}; do if [ "${v1_parts[i]:-0}" -gt "${v2_parts[i]:-0}" ]; then return 0 # ver1 is greater elif [ "${v1_parts[i]:-0}" -lt "${v2_parts[i]:-0}" ]; then return 1 # ver1 is not greater fi done return 1 # versions are equal } # Test cases (can be commented out in production) test_versions() { local test_pairs=( "6.7 6.6.2" # should return 0 (true) "7 6.7" # should return 0 (true) "6.6.2 6.6.2" # should return 1 (false) "6.6.1 6.6.2" # should return 1 (false) "6.7.0 6.7" # should return 1 (false) "6.7.1 6.7" # should return 0 (true) "6.7.0.1 6.7" # should return 0 (true) ) echo "Running version comparison tests..." for pair in "${test_pairs[@]}"; do read -r v1 v2 <<< "$pair" if version_greater_than "$v1" "$v2"; then echo "$v1 > $v2 (OK)" else echo "$v1 <= $v2" fi done echo "Running version validation tests..." local test_versions=( "6" # valid "6.7" # valid "6.7.2" # valid "6.7.2.1" # valid "6.7.2.1.5" # invalid "6.7.2a" # invalid "6.7." # invalid ".6.7" # invalid "a.b.c" # invalid ) for ver in "${test_versions[@]}"; do if is_valid_version "$ver"; then echo "$ver is valid" else echo "$ver is invalid" fi done } # Uncomment to run tests ## test_versions # Get the current WordPress version current_wp_version=$(get_wp_version | tr -d '\r') if [ -z "$current_wp_version" ] || ! is_valid_version "$current_wp_version"; then echo "Failed to get valid WordPress version" exit 1 fi echo "Using WordPress version: $current_wp_version" ## current_wp_version="6.7.0" # Check the current IMAGE_VERSION from the .env file echo "Reading IMAGE_VERSION from .env file..." env_wp_version=$(grep '^IMAGE_VERSION=' "$ENV_FILE" | cut -d= -f2 | tr -d '"' | tr -d "'") if ! is_valid_version "$env_wp_version"; then echo "Invalid version in .env file" exit 1 fi echo "WordPress version in .env file (IMAGE_VERSION): $env_wp_version" # Check if current_wp_version is greater than env_wp_version if version_greater_than "$current_wp_version" "$env_wp_version"; then echo "Upgrade detected: WordPress ($current_wp_version) is newer than .env ($env_wp_version)" # Find the best available version to use target_version=$(find_closest_version "$current_wp_version" | tr -d '\r') if [ -n "$target_version" ]; then echo "Found appropriate Docker image version: $target_version" # Backup current .env file echo "Backing up current .env file..." ### cp "$ENV_FILE" "${ENV_FILE}.backup" # Update the IMAGE_VERSION in the .env file echo "Updating IMAGE_VERSION in .env file to: $target_version" ### sed -i "s/^IMAGE_VERSION=.*$/IMAGE_VERSION=\"$target_version\"/" "$ENV_FILE" # Check if IMAGE_VERSION_HOLD exists, add it if it doesn't if ! grep -q "^IMAGE_VERSION_HOLD=" "$ENV_FILE"; then echo "Adding IMAGE_VERSION_HOLD=true after IMAGE_VERSION..." ### sed -i "/^IMAGE_VERSION=.*/a IMAGE_VERSION_HOLD=true" "$ENV_FILE" fi echo "Successfully updated .env to version $target_version" else echo "Failed to find an appropriate version on Docker Hub" exit 1 fi else echo "No update required. WordPress version ($current_wp_version) is not newer than .env version ($env_wp_version)." fi echo "WordPress version check and update script completed."