test/bin/check-wordpressversion

361 lines
13 KiB
Bash
Executable File

#!/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}.bck-versionprotect"
# 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."