Skip to content

Instantly share code, notes, and snippets.

@keesiemeijer
Last active November 17, 2023 07:08
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save keesiemeijer/1286ab6695462cf6cf793569c6c356b8 to your computer and use it in GitHub Desktop.
Save keesiemeijer/1286ab6695462cf6cf793569c6c356b8 to your computer and use it in GitHub Desktop.
A bash script to update everything WordPress (core, plugins, themes and comments).
#!/usr/bin/env bash
# =============================================================================
#
# *** WARNING: THIS SCRIPT IS NO LONGER MAINTAINED ***
#
# use https://github.com/keesiemeijer/wp-update instead
#
# =============================================================================
# =============================================================================
# DESCRIPTION
#
# A bash script to update WordPress core, plugins, themes and comments via SSH.
#
# Features:
# All updates are displayed before updating
# Interactive prompts keeps you in control of what gets updated
# Database and file backups (plugins, themes) are created when updates are made
# Manage spam and trash comments
#
# =============================================================================
# =============================================================================
# REQUIREMENTS
#
# WP-CLI
# SSH access to the server.
#
# Install WP-CLI if not installed. The command `wp` should be
# executable and in your PATH (e.g. /usr/local/bin/).
# See the installation instructions for WP-CLI: http://wp-cli.org/#installing
#
# If you have permission issues when moving it in your Path see https://stackoverflow.com/a/14650235
# =============================================================================
# =============================================================================
# INSTALLATION
#
# 1 Download this script
# curl -o wp-update https://gist.githubusercontent.com/keesiemeijer/1286ab6695462cf6cf793569c6c356b8/raw
#
# 2 Edit and point the DOMAINS_PATH variable (below this section) to a
# parent directory with WP sites in it.
# Example:
# domains <- point it here
# ├── wp-site1 (directory)
# │ └── WP files
# └── wp-site2 (directory)
# └── WP files
#
# 3 Upload this file to your server and log in with ssh.
#
# 4 Go to where you uploaded it and make this file executable
# chmod +x wp-update
#
# 5 Move it in your path like you did with WP-CLI (e.g /usr/local/bin/).
# If you have permission issues when moving it in your Path see: https://stackoverflow.com/a/14650235
# mv wp-update /usr/local/bin/wp-update
#
# Now you can use the `wp-update` command to update your sites.
# test it out by using "wp-update --help"
# ============================================================================
# Edit this variable to point to a directory with WordPress directories in it (see above)
readonly DOMAINS_PATH="$HOME/domains"
# =============================================================================
# USAGE
#
# wp-update <site-directory> [option...]
#
# Use the site directory name for a WordPress site
# Use "wp-update --help" to see what options are available
#
# Without options the plugins and themes are updated. Example:
#
# wp-update <site-directory>
#
# The same example, but with options used:
#
# wp-update <site-directory> --plugins --themes
#
# Example to update everything
#
# wp-update <site-directory> --all
#
# =============================================================================
# =============================================================================
# BACKUPS
#
# Backups are only created when something is updated.
#
# Database backups are created before and after updating.
# They are saved in the root directory of your website.
# Test the backups that are made by this script before you rely on this feature.
#
# Plugin and theme folder backups are made before updating plugins or themes.
# They are saved in the wp-content folder of your website
#
# Newer backups replace previous backups as not to clutter your website.
# =============================================================================
if [ $# -lt 1 ]; then
printf "\nUsage: %s <website-directory> [option...]\n" "$0"
printf "Example: wp-update my-website --plugins\n\n"
printf "Use \"wp-update --help\" to see all options\n"
exit 1
fi
# =============================================================================
# Variables
# =============================================================================
# Website directory
readonly WEBSITE=$1
# Does a database backup already exist.
DATABASE_BACKUP=false
# Do translations need updating (after updating core, plugins or themes).
UPDATE_TRANSLATIONS=false
# Use a prompt before updating plugins, themes and comments.
USE_PROMPT=true
# Associative array to check CLI option
declare -A OPTIONS
# All unique CLI argument options in the right order
ALLOPTIONS=()
# =============================================================================
# Functions
# =============================================================================
function is_file() {
local file=$1
[[ -f $file ]]
}
function is_dir() {
local dir=$1
[[ -d $dir ]]
}
function add_type_option(){
local option=$1
if ! [[ ${OPTIONS["$option"]} ]] ; then
OPTIONS["$option"]=1
ALLOPTIONS+=("$option")
fi
}
function maybe_do_database_backup(){
if [[ "$DATABASE_BACKUP" = true || "$DATABASE_BACKUP" = 'none' ]]; then
return 1
fi
if ! make_database_backup "before"; then
printf "Creating a database backup before updating failed"
exit 1
fi
return 0
}
function make_database_backup(){
local prefix=$1
if is_file "${prefix}_update_${WEBSITE}.sql"; then
printf "Removing a previous database backup file...\n"
rm "${prefix}_update_${WEBSITE}.sql"
fi
printf "Creating a backup of the %s database ${prefix} updating...\n" "$WEBSITE"
wp db export "${prefix}_update_${WEBSITE}.sql" --allow-root
if ! is_file "${prefix}_update_${WEBSITE}.sql"; then
printf "\e[31mWarning: No database backup file found in: %s\033[0m\n" "$DOMAINS_PATH/$WEBSITE"
return 1
fi
if ! [[ -s "${prefix}_update_${WEBSITE}.sql" ]]; then
printf "\e[31mWarning: database backup file is empty in: %s\033[0m\n" "$DOMAINS_PATH/$WEBSITE"
return 1
fi
DATABASE_BACKUP=true
return 0
}
function wp_core_is_installed(){
# Check for wp-config.php file
if ! is_file "wp-config.php"; then
return 1
fi
# Check if WP tables exist
wp core is-installed --allow-root 2> /dev/null
}
function update_wp_core(){
printf "Checking WordPress version...\n"
update=$(wp core check-update --field=version --format=count --allow-root)
if [[ -z $update ]]; then
printf "WordPress is at the latest version\n"
return 0
fi
maybe_do_database_backup
printf "Updating WordPress\n"
wp core update --allow-root
UPDATE_TRANSLATIONS=true
}
function update_language(){
printf "Checking translations...\n"
update=$(wp core language list --update=available --format=count --allow-root)
if [[ $update = 0 ]]; then
printf "No language updates available\n"
return 0
fi
maybe_do_database_backup
printf "Updating translations\n"
wp core language update --allow-root
}
function update_asset(){
local asset_type=$1
local update
printf "Checking %s updates...\n" "$asset_type"
update=$(wp "$asset_type" list --update=available --number=1 --format=count --allow-root)
if [[ $update = 0 ]]; then
printf "No %s updates available\n" "$asset_type"
return 0
fi
printf "Updating %ss\n" "$asset_type"
if [[ "$USE_PROMPT" = true ]]; then
wp "$asset_type" update --all --dry-run --allow-root
read -p "Do you want to update all ${asset_type}s [y/n]" -r
if ! [[ $REPLY = "Y" || $REPLY = "y" ]]; then
printf "%s updates aborted\n" "$asset_type"
return 1
fi
fi
printf "backing up %ss\n" "$asset_type"
if is_file "$DOMAINS_PATH/$WEBSITE/wp-content/${asset_type}s-backup"; then
rm -rf "$DOMAINS_PATH/$WEBSITE/wp-content/${asset_type}s-backup"
fi
cp -r "$DOMAINS_PATH/$WEBSITE/wp-content/${asset_type}s" "$DOMAINS_PATH/$WEBSITE/wp-content/${asset_type}s-backup"
maybe_do_database_backup
printf "Updating %ss\n" "$asset_type"
wp "$asset_type" update --all --allow-root
UPDATE_TRANSLATIONS=true
return 0
}
function update_comments() {
local status=$1
printf "Checking %s comments...\n" "$status"
count=$(wp comment list --number=1 --status="$status" --format=count --allow-root)
if [[ $count = 0 ]]; then
printf "No %s comments found\n" "$status"
return 0
fi
if [[ "$USE_PROMPT" = true ]]; then
wp comment list --status="$status" --fields=ID,comment_author,comment_author_email,comment_approved,comment_content --allow-root
read -p "Do you want to delete all ${status} comments [y/n]" -r
if ! [[ $REPLY = "Y" || $REPLY = "y" ]]; then
printf "Deleting %s comments aborted\n" "$status"
return 1
fi
fi
maybe_do_database_backup
printf "Deleting %s comments\n" "$status"
wp comment delete $(wp comment list --status="$status" --format=ids --allow-root) --allow-root
return 0
}
# =============================================================================
# Options
# =============================================================================
while test $# -gt 0; do
if ! [[ "$1" =~ ^- ]]; then
# Not starting with a dash (not an option).
shift
else
case "$1" in
-h|--help)
printf "\nwp-update usage:\n"
printf "\twp-update <website-directory> [option...]\n\n"
printf "wp-update example:\n"
printf "\twp-update my-website --plugins\n\n"
printf "Options controlling update type:\n"
printf -- "\t-w, --core Update WordPress core\n"
printf -- "\t-p, --plugins Update plugins\n"
printf -- "\t-t, --themes Update themes\n"
printf -- "\t-l, --translations Update translations\n"
printf -- "\t-c, --comments Update comments\n"
printf -- "\t-a, --all Update everything\n\n"
printf "\tWithout an update type option plungins and themes are updated\n\n"
printf "Options extra:\n"
printf -- "\t-h, --help Show help\n"
printf -- "\t-f, --force Force update (without a prompt to abort updating (themes, plugins, comments)\n"
printf -- "\t-x, --no-db-backup Don't make a database backup before and after updating\n\n"
exit 0
;;
-f|--force) USE_PROMPT=false ;;
-x|--no-db-backup) DATABASE_BACKUP='none' ;;
-w|--core) add_type_option "core" ;;
-p|--plugins) add_type_option "plugins" ;;
-t|--themes) add_type_option "themes";;
-l|--translations) add_type_option "translations" ;;
-c|--comments) add_type_option "comments";;
-a|--all) add_type_option "all" ;;
*)
printf "Unknown option: %s. Use \"wp-update --help\" to see all options\n" "$1"
exit 1
;;
esac
shift
fi
done
# Check if options is empty
if [[ ${#ALLOPTIONS[@]} -eq 0 ]]; then
ALLOPTIONS=("assets")
fi
# Check if `all` was used
if [[ ${OPTIONS["all"]} ]]; then
ALLOPTIONS=("all")
fi
# =============================================================================
# Start updates
# =============================================================================
if ! is_dir "$DOMAINS_PATH/$WEBSITE"; then
printf "Could not find Website directory: %s\n" "$WEBSITE"
exit 1
fi
cd "$DOMAINS_PATH/$WEBSITE" || exit
if ! wp_core_is_installed; then
printf "No WordPress website found in: %s\n" "$DOMAINS_PATH/$WEBSITE"
exit 1
fi
# Check network connection for options that need it.
if [[ ${OPTIONS["all"]} || ${OPTIONS[plugins]} || ${OPTIONS["themes"]} || ${OPTIONS["core"]} || ${OPTIONS["translations"]} ]]; then
printf "Checking network connection...\n"
if ! ping -c 3 -W 5 8.8.8.8 >> /dev/null 2>&1; then
# Bail if there is no internet connection
printf "No network connection detected\n\n"
exit 1
else
printf "Network connection detected\n"
fi
fi
for type in "${ALLOPTIONS[@]}"
do
case "$type" in
assets)
update_asset "plugin"
update_asset "theme"
;;
core)
update_wp_core
;;
plugins)
update_asset "plugin"
;;
themes)
update_asset "theme"
;;
comments)
update_comments "spam"
update_comments "trash"
;;
all)
update_asset "plugin"
update_asset "theme"
update_wp_core
update_comments "spam"
update_comments "trash"
;;
*)
if ! [[ 'translations' = "$type" ]]; then
printf "Unknown option: %s. Use \"wp-update --help\" to see all options\n" "$type"
exit 1
fi
esac
done
# Translations are updated at the end (if needed)
if [[ "$UPDATE_TRANSLATIONS" = true ]]; then
update_language
else
if [[ ${OPTIONS["translations"]} ]]; then
update_language
fi
fi
if [[ "$DATABASE_BACKUP" = true ]]; then
if ! make_database_backup "after"; then
printf "Creating a database backup after updating failed"
exit 1
fi
fi
printf "Finished updating\n"
@locougil
Copy link

Hi,
Thanks a lot for your work, it's working perfectly ;)
Is it possible to loop through all the site directories and update all the sites in one command line ?
like wp-update --all and it will update suball the sub directories from the variable readonly DOMAINS_PATH="$HOME/domains" ?

Hope to hear from you soon
Kind regards
Loïc

@keesiemeijer
Copy link
Author

Hi @locougil

I've moved this script to a github repo, and I'm not maintaining this script anymore.
https://github.com/keesiemeijer/wp-update

You can loop through directories with a one-liner like this.

for d in path/to/directory/*/ ; do wp-update "$d"; done

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment