System requirements:
- User uploads
$wgUploadDirectory
are stored offsite - Non-Docker MediaWiki with normal setup
- Composor installed (Can be installed automatically during updating)
Goals:
- Update MediaWiki with nearly zero downtime
- Download and install latest tagged MediaWiki from tarball package
- Update extensions and skins from latest git tagged branch
- Install extension-specific dependencies during updating
First create a script name it mw-update.sh
and make sure the script exits if anything goes wrong:
# Exit the whole script if anything goes wrong
set -e
Create a config file mw-update.conf
with the following content:
version=1.42.1
tmp="/tmp"
dest_base="/srv/www/wikiroot"
dest="/public_html"
permissions="33.33"
mode="docker"
docker_container_name="docker-php-1"
docker_dest_base="/app/wikiroot/public_html"
extensions="
BetaFeatures
CheckUser
CommonsMetadata
MobileFrontend
TorBlock
Popups
AntiSpoof
WikiLove
cldr
Flow
ContributionScores
intersection
UploadWizard
RevisionSlider
"
skins="
MinervaNeue
"
Switch back to your script. Add more variables to use later:
# Exit the whole script if anything goes wrong
set -e
MWU_START=$(date +%s)
# Set the current working directory to the directory of this script
# http://stackoverflow.com/a/17744637/412385
cd "${0%/*}"
# Check if config exists
if [ ! -f ./mw-update.conf ]; then
echo -e "Config not found, run the following command first:"
echo -e "\n$ cp mw-update.sample.yml mw-update.yml"
exit 1
fi
# Parse config
if [ -f "mw-update.conf" ]; then
source "mw-update.conf"
else
echo "Error: Configuration file not found."
exit 1
fi
# MediaWiki uses git tag for latest stable version
MW_VER=$version
# Decoration
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Production dir
PROD_BASE="$dest_base"
PROD_DIR="$PROD_BASE$dest"
# `MW_VER_MAIN` is for the stupid download URL
MW_VER_MAIN=$(echo "$MW_VER" | sed -E 's/\.[0-9]+$//g')
# Temp dir prepare for update
TMP_BASE="$tmp/mediawiki-update"
TMP_DIR="$TMP_BASE/mediawiki-$MW_VER"
# Extensions dir
EXT_DIR="$TMP_DIR/extensions"
SKIN_DIR="$TMP_DIR/skins"
# Extensions use git branch for latest stable version
EXT_VER=$(echo "REL$MW_VER_MAIN" | sed -E 's/\./_/g')
# List of custom extensions
EXTENSIONS="$extensions"
SKINS="$skins"
Then print the setup info for confirmation:
# Prompt before executing anything
echo -e "${BLUE}MediaWiki Auto Updater${NC}"
echo -e "Sparanoid ([email protected])\n"
echo -e " Running mode: ${BLUE}$mode${NC}"
echo -e " Core version: ${BLUE}$MW_VER${NC}"
echo -e " Branch version: ${BLUE}$MW_VER_MAIN${NC}"
echo -e "Extension version: ${BLUE}$EXT_VER${NC}"
echo -e " Temp directory: ${BLUE}$TMP_BASE${NC}"
echo -e " Destination base: ${BLUE}$PROD_BASE${NC}"
echo -e " Destination: ${BLUE}$PROD_DIR${NC}"
echo -e " Docker Directory: ${BLUE}$docker_dest_base${NC}"
echo -e " Container name: ${BLUE}$docker_container_name${NC}"
echo -e " Permissions: ${BLUE}$permissions${NC}"
echo -e "Custom extensions: ${BLUE}$EXTENSIONS${NC}"
echo -e " Custom skins: ${BLUE}$SKINS${NC}"
Check if PHP Composor installed:
function check_composer() {
# Allow running Composer with root
export COMPOSER_ALLOW_SUPERUSER=1
if command -v composer >/dev/null 2>&1 ; then
echo -e "${BLUE}PHP Composer found: $(composer --version | head -n 1)${NC}"
else
echo -e "PHP Composer not found, trying to install it..."
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
php composer-setup.php
php -r "unlink('composer-setup.php');"
echo -e "Move PHP Composer to /usr/local/bin/composer for global use"
mv composer.phar /usr/local/bin/composer
fi
}
if [[ $* == *--clean* ]]; then
echo -e "\033[0;31mCLEAN MODE: ALL FILES UNDER ${TMP_BASE} WILL BE DELETED!${NC}"
fi
read -p "Press enter to continue..."
echo -e "\n"
if [[ $* == *--clean* ]]; then
echo -e "${BLUE}Removing update leftovers (--clean)${NC}"
rm -rf $TMP_BASE
fi
Begin to update MediaWiki cores:
# Update core files
echo -e "${BLUE}Creating essential directories${NC}"
mkdir -p $TMP_BASE
echo -e "${BLUE}Updating MediaWiki core files${NC}"
cd $TMP_BASE
wget -c "https://releases.wikimedia.org/mediawiki/$MW_VER_MAIN/mediawiki-$MW_VER.tar.gz"
tar -zxf mediawiki-$MW_VER.tar.gz
echo -e "${BLUE}Backing up LocalSettings.php${NC}"
cd $TMP_DIR
cp $PROD_DIR/LocalSettings.php $TMP_DIR/
Update extensions:
# Update extensions
for extension in $EXTENSIONS
do
echo -e "${BLUE}Updating extension $extension...${NC}"
cd $EXT_DIR
if [ ! -d "$EXT_DIR/$extension" ]; then
echo -e "${EXT_DIR}/${extension} git repo does not exists!"
git clone "https://gerrit.wikimedia.org/r/mediawiki/extensions/$extension.git"
fi
cd "$EXT_DIR/$extension"
git reset --hard
git clean -f -d
git pull
# shellcheck disable=SC2086
git checkout $EXT_VER
echo ""
done
Some extensions need special setup process like update git submodules or install dependencies with Composor:
# Extension specific process
# No longer required. VisualEditor is now bundled with MediaWiki 1.35 and above
if [[ ${extensions} =~ "VisualEditor" ]]; then
echo -e "${BLUE}Updating submodules for VisualEditor...${NC}"
cd $EXT_DIR/VisualEditor
git submodule update --init
echo ""
fi
if [[ ${extensions} =~ "Flow" ]]; then
echo -e "${BLUE}Updating composer for Flow (StructuredDiscussions)...${NC}"
cd $EXT_DIR/Flow
if [[ ${mode} == "docker" ]]; then
docker run --rm -it -v "$PWD":/app composer update --no-dev
fi
if [[ ${mode} == "local" ]]; then
check_composer
composer update --no-dev
fi
echo ""
fi
if [[ ${extensions} =~ "AntiSpoof" ]]; then
echo -e "${BLUE}Updating composer for AntiSpoof...${NC}"
cd $EXT_DIR/AntiSpoof
if [[ ${mode} == "docker" ]]; then
docker run --rm -it -v "$PWD":/app composer update --no-dev
fi
if [[ ${mode} == "local" ]]; then
check_composer
composer update --no-dev
fi
echo ""
fi
Update skins:
<span class="hljs-meta prompt_"># </span><span class="language-bash">Update skinsfor skin <span class="hljs-keyword">in</span> <span class="hljs-variable">$SKINSdo</span> <span class="hljs-built_in">echo</span> -e <span class="hljs-string">"<span class="hljs-variable">${BLUE}</span>Updating skin <span class="hljs-variable">$skin</span>...<span class="hljs-variable">${NC}</span>"</span> <span class="hljs-built_in">cd</span> <span class="hljs-variable">$SKIN_DIR</span> <span class="hljs-keyword">if</span> [ ! -d <span class="hljs-string">"<span class="hljs-variable">$SKIN_DIR</span>/<span class="hljs-variable">$skin</span>"</span> ]; <span class="hljs-keyword">then</span> git <span class="hljs-built_in">clone</span> https://gerrit.wikimedia.org/r/mediawiki/skins/<span class="hljs-variable">$skin</span>.git <span class="hljs-keyword">fi</span> <span class="hljs-built_in">cd</span> <span class="hljs-variable">$SKIN_DIR</span>/<span class="hljs-variable">$skin</span> git reset --hard git clean -f -d git pull git checkout <span class="hljs-variable">$EXT_VER</span> <span class="hljs-built_in">echo</span> <span class="hljs-string">""</span><span class="hljs-keyword">done</span></span>
Please note that all the above process are done in $TMP_BASE
directory. The website is still untouched at the moment. Now lets go into $TMP_BASE
to finalize the prepare process:
cd $TMP_BASE
echo -e "${BLUE}Backing up old files...${NC}"
# tar mediawiki-$MW_VER-update-backup.tar.gz -C $PROD_DIR .
mv $PROD_DIR "$PROD_BASE/mediawiki-$MW_VER-backup-$(date +%F-%H:%M)"
echo -e "${BLUE}Moving updated files to production...${NC}"
cp -R $TMP_DIR $PROD_DIR
echo -e "${BLUE}Updating directory permissions ($permissions)...${NC}"
chown -R $permissions $PROD_DIR
echo -e "${BLUE}Running MediaWiki maintenance ($mode mode)...${NC}"
if [[ ${mode} == "docker" ]]; then
docker exec $docker_container_name php $docker_dest_base/maintenance/update.php --quick
fi
if [[ ${mode} == "local" ]]; then
php $PROD_DIR/maintenance/update.php --quick
fi
MWU_END=$(date +%s)
MWU_RUNTIME=$((MWU_END - MWU_START))
echo -e "${BLUE}Done! Time took: ${MWU_RUNTIME}s${NC}"