Skip to content

Instantly share code, notes, and snippets.

@gammy
Created June 12, 2020 21:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gammy/1cc3b8e72d471ac7e185a00bf93c590b to your computer and use it in GitHub Desktop.
Save gammy/1cc3b8e72d471ac7e185a00bf93c590b to your computer and use it in GitHub Desktop.
DokuWiki upgrade helper which takes conf and data directories into account.
#!/usr/bin/env bash
# Gammies grimey dokuwiki updater.
#
# This script is dumb, but not as dumb as the upgrade plugin.
# Use with caution: I may not have considered all possible scenarios.
# One thing this script does not check is adequate permission to create files
# and subdirectories. Just basic sanity is performed.
#
# Written mainly for my own needs.
web_user=www-data
web_group=www-data
backup_dir=/tmp
do_backup=1
show_usage()
{
local me=$(basename $0)
echo "Gammies dokuwiki updater"
echo "Usage: $me [opts] <dokuwiki root> <dokuwiki tarball>"
echo "Example: $me -b -c ~/dokuwiki_conf /public_html/wiki /tmp/dokuwiki-rc.tgz"
echo
echo "You WILL be prompted before the update procedure begins."
echo
echo "Options:"
echo "-s Do NOT crate a backup tarball of all specified directories"
echo "-b <path> Path to place backup tarball (default: $backup_dir)"
echo "-w <path> Path to (dirty) working directory; defaults to 'mktemp -d'"
echo "-c <path> Path to non-default 'dokuwiki_conf' directory"
echo "-d <path> Path to non-default 'dokuwiki_data' directory"
echo "-u <user> Use <user> as the web-process owner (default: $web_user)"
echo "-g <group> Use <group> as the web-process group (default: $web_group)"
echo "-h Show this message"
exit 0
}
wrap_realpath()
{
# Although realpath will return a non-existing file, it will
# error out if a subdirectory is specified where the parent doesn't exit
local rp=$(realpath "$1" 2>/dev/null)
if [ -z "$rp" ]
then
echo "$1"
else
echo "$rp"
fi
}
if [ $# -lt 2 ]
then
show_usage
exit 0
fi
deps=(find rsync wc whoami grep awk tar cat)
deps_missing=
for dep in ${deps[@]}
do
# While GNU Which prints its own nice message to stderr if the program is
# missing, other implementations do not.
# We thus show the message ourselves based on the errorcode.
if ! which $dep > /dev/null 2>&1
then
echo "Missing dependency: $dep" >&2
deps_missing=y
fi
done
if [ -n "$deps_missing" ]
then
echo "Fatal: Unable to continue" >&2
exit 1
fi
noopt_args=()
work_dir=
data_dir=
conf_dir=
wiki_dir=
while [ $# -gt 0 ]
do
case "$1" in
-s) do_backup= ;;
-b) backup_dir="$2"; shift ;;
-w) work_dir="$2"; shift ;;
-c) conf_dir="$2"; shift ;;
-d) data_dir="$2"; shift ;;
-u) web_user="$2"; shift ;;
-g) web_group="$2"; shift ;;
-h) show_usage; exit 0 ;;
*) noopt_args+=($1) ;;
esac
shift
done
if [ ${#noopt_args[@]} -ne 2 ]
then
show_usage
exit 0
fi
wiki_dir="${noopt_args[0]}"
tar_file="${noopt_args[1]}"
[ -z "$data_dir" ] && data_dir="$wiki_dir/data"
[ -z "$conf_dir" ] && conf_dir="$wiki_dir/conf"
[ -z "$work_dir" ] && work_dir=$(mktemp -d)
backup_dir=$(wrap_realpath "$backup_dir")
work_dir=$(wrap_realpath "$work_dir")
data_dir=$(wrap_realpath "$data_dir")
conf_dir=$(wrap_realpath "$conf_dir")
wiki_dir=$(wrap_realpath "$wiki_dir")
tar_file=$(wrap_realpath "$tar_file")
echo "Paths"
echo "Tarball : '$tar_file'"
echo "Wiki root path: '$wiki_dir'"
echo "Data path : '$data_dir'"
echo "Config path : '$conf_dir'"
echo "Work path : '$work_dir'"
echo "Backup path : '$backup_dir'"
echo
echo "Users / Groups"
echo "Web user : '$web_user'"
echo "Web group : '$web_group'"
echo "Current user : '$(whoami)'"
echo "Current groups: '$(groups)'"
echo
echo -n "Perform backup: "
if [ -n "$do_backup" ]; then echo "Yes"; else echo "NO"; fi
echo
# Sanity
echo "Basic sanity check"
echo -n "Web user exists : "
if grep -q "$web_user" /etc/passwd
then
echo "Yes"
else
echo "NO"
echo "Fatal: $web_user: No such user" >&2
exit 1
fi
echo -n "Web group exists: "
if grep -q "$web_group" /etc/group
then
echo "Yes"
else
echo "NO"
echo "Fatal: $web_group: No such group" >&2
exit 1
fi
ok=1
for f in "$wiki_dir" "$data_dir" "$conf_dir"
do
if [ -d "$f" ]
then
echo "$f: exists and is a directory"
if [ -r "$f" ] && [ -w "$f" ]
then
echo "$f: read/write access OK"
else
ok=
echo "$f: read and/or write access not granted" >&2
fi
else
ok=
echo "$f: doesn't exist or isn't accessbile" >&2
fi
done
if [ -f "$tar_file" ]
then
if [ -r "$tar_file" ]
then
echo "$tar_file: read access OK"
else
ok=
echo "$tar_file: read access not granted" >&2
fi
else
ok=
echo "$tar_file: doesn't exist or isn't a file" >&2
fi
if [ -z "$ok" ]
then
echo "Encountered one or more fatal errors" >&2
exit 1
fi
echo -n "Preload config matching configuration path: "
if [ -e "$wiki_dir/inc/preload.php" ]
then
preload_conf=$(grep ^define $wiki_dir/inc/preload.php \
| grep DOKU_CONF \
| cut -d"'" -f 4)
preload_conf=$(realpath "$preload_conf")
if [ "$preload_conf" = "$conf_dir" ]
then
echo "Yes"
else
echo "NO"
echo
echo -e "\n*** WARNING WARNING WARNING ***" >&2
echo "$wiki_dir/inc/preload.php: "
echo "The DOKU_CONF parameter differs from the user-specified path:"
echo " DOKU_CONF: '$preload_conf'"
echo " Specified: '$conf_dir'"
echo -e "\n*** WARNING WARNING WARNING ***" >&2
echo
fi
else
echo "NO"
echo "WARNING: $wiki_dir/inc/preload.php: File not found" >&2
echo "This might indicate that the wiki path specified is incorrect,"
echo "or that you are trying to perform an upgrade on a fresh install."
echo
fi
# Working directory
if [ ! -d "$work_dir" ]
then
echo "Fatal: '$work_dir': Failed to access unpack path" >&2
echo "If one was supplied on the commandline, you must create it yourself" >&2
exit 1
fi
tcount=$(find $work_dir -maxdepth 1 | wc -l)
if [ "$tcount" != "1" ]
then
echo -e "\n*** WARNING WARNING WARNING ***" >&2
echo "$work_dir: Working directory not empty" >&2
echo "Make sure the correct path has been supplied or generated"
echo -e "*** WARNING WARNING WARNING ***\n" >&2
echo "Press RETURN to continue, or ^C to abort"
read foo
fi
# Backup
if [ -n "$do_backup" ]
then
# Caveat emptor
echo
echo "Ready to start the backup process."
echo "Press RETURN to continue, or ^C to abort"
read foo
echo "Creating backup"
backup_file=$(date +"$backup_dir/%y%m%d_%H%M%S_DokuWiki_Backup.tgz")
tar cfz "$backup_file" "$wiki_dir" "$data_dir" "$conf_dir" || exit 1
echo "Backup OK: $backup_file"
fi
echo
echo "Ready to start unpacking the tarball."
echo "Press RETURN to continue, or ^C to abort"
read foo
# Unpack
echo "Unpacking '$tar_file' to '$work_dir'"
cd "$work_dir" || exit 1
tar zxf "$tar_file" || exit 1
unpack_root="$(ls -1 | tail -n1 | grep ^dokuwiki-)"
unpack_root=$(realpath "$work_dir/$unpack_root")
if [ ! -d "$unpack_root" ]
then
echo "Unable to find root directory in unpack path $work_dir" >&2
echo "Is the supplied tarball really a dokuwiki package?" >&2
exit 1
fi
echo "Unpacked root: '$unpack_root'"
# Check dokuwiki.php just in case
if ! cmp "$unpack_root/conf/dokuwiki.php" "$conf_dir/dokuwiki.php"
then
echo -e "\n*** WARNING WARNING WARNING ***" >&2
echo "The default configuration file 'dokuwiki.php' differs from the one "
echo "located in $conf_dir."
echo "This might be something mundane, or it might hint at local changes"
echo "having been mistakenly been made to $conf_dir/mediawiki.php when"
echo "they should have been added to $conf_dir/local.php"!
echo "mediawiki.php should NOT be changed; local.php overrides it."
echo "Diff:"
diff "$conf_dir/dokuwiki.php" "$unpack_root/conf/dokuwiki.php"
echo -e "\n*** WARNING WARNING WARNING ***" >&2
echo
echo "Press RETURN to continue, or ^C to abort"
echo
fi
# Delete deprecated files
del_file="$unpack_root/data/deleted.files"
del_queue_file="$work_dir/deletion_queue"
[ -e "$del_queue_file" ] && rm -v "$del_queue_file"
if [ ! -f "$del_file" ]
then
echo "$del_file: Not found (but was expected)." >&2
echo "Aborting because I'm scared." >&2
exit 1
fi
echo "Checking deprecated data files"
grep -vE '^($|#)' "$del_file" | grep ^data/ | while read f
do
real_file="$data_dir/$f"
# echo "DATA: $real_file"
if [ -e "$real_file" ]
then
echo "$real_file" >> "$del_queue_file"
echo "$real_file: Marked for deletion"
fi
done
echo "Checking deprecated configuration files"
grep -vE '^($|#)' "$del_file" | grep ^conf/ | while read f
do
real_file="$conf_dir/$f"
# echo "CONF: $real_file"
if [ -e "$real_file" ]
then
echo "$real_file" >> "$del_queue_file"
echo "$real_file: Marked for deletion"
fi
done
echo "Checking deprecated wiki files"
grep -vE '^($|#)' "$del_file" | grep -v -e ^conf/ -e ^data/ | while read f
do
real_file="$wiki_dir/$f"
# echo "WIKI: $real_file"
if [ -e "$real_file" ]
then
echo "$real_file" >> "$del_queue_file"
echo "$real_file: Marked for deletion"
fi
done
echo
deletion_queued_count=0
if [ -e "$del_queue_file" ]
then
deletion_queued_count=$(wc -l "$del_queue_file" | awk '{print $1}')
if [ "$deletion_queued_count" -gt 0 ]
then
echo "$deletion_queued_count files will be deleted:"
cat "$del_queue_file"
else
echo "No files queued for deletion"
fi
fi
# Caveat emptor
echo
echo "Ready to begin the upgrade process."
echo "Press RETURN to continue, or ^C to abort"
read foo
# Copy
(
echo "---------------------------------------------------------------"
echo "Syncing configuration files"
echo "Source: $unpack_root/conf"
echo "Target: $conf_dir"
cd "$unpack_root/conf" || exit 1
rsync -acv ./ "$conf_dir/" || exit 1
echo
)
(
echo "---------------------------------------------------------------"
echo "Syncing data files"
echo "Source: $unpack_root/data"
echo "Target: $data_dir"
cd "$unpack_root/data" || exit 1
rsync -acv ./ "$data_dir/" || exit 1
echo
)
(
echo "---------------------------------------------------------------"
echo "Syncing wiki files"
echo "Source: $unpack_root"
echo "Target: $wiki_dir"
cd "$unpack_root" || exit 1
rsync -acv --exclude 'data' --exclude 'conf' ./ "$wiki_dir/" || exit 1
echo
)
# Delete deprecated
if [ "$deletion_queued_count" -gt 0 ]
then
echo "Deleting deprecated files"
cat "$del_queue_file" | xargs rm -v
fi
# Permissions
echo "Updating permissions"
perm_change_file="$work_dir/permission_changes"
if [ -e "$perm_change_file" ]
then
rm -v "$perm_change_file"
fi
# Based on info from:
# https://www.dokuwiki.org/install:permissions
# "data/ and data/tmp/ directory: All files in and below these directories
# must be writable by the web process for DokuWiki to work."
#echo "Perms: $data_dir"
chown -Rv "$web_user:$web_group" "$data_dir" 2>&1 \
| grep -v 'retained as' >> "$perm_change_file"
# "lib/ directory: This directory must be readable by the public for style
# sheets to display. 755 works fine."
#echo "Perms: $wiki_dir/lib"
chmod -v 755 "$wiki_dir/lib" 2>&1 \
| grep -v 'retained as' >> "$perm_change_file"
# "lib/tpl directory must be writable for the webprocess to install templates"
#echo "Perms: $wiki_dir/lib/tpl"
chown -v "$web_user:$web_group" "$wiki_dir/lib/tpl" 2>&1 \
| grep -v 'retained as' >> "$perm_change_file"
# Note: the permissions of files in conf/, as set from the dokuwiki tarball,
# are set to 1001:1002 by default, i.e commonly the first available
# users on a UNIX system. Point this out.
# "conf/ directory, following files must be writable by the web process:"
for f in 'local.php' \
'local.php.bak' \
'users.auth.php' \
'acl.auth.php' \
'plugins.local.php' \
'plugins.local.php.bak'
do
# echo "Check: $conf_dir/$f"
if [ -e "$conf_dir/$f" ]
then
chown -v "$web_user:$web_group" "$conf_dir/$f" 2>&1 \
| grep -v 'retained as' >> "$perm_change_file"
chmod -v 755 "$conf_dir/$f" 2>&1 \
| grep -v 'retained as' >> "$perm_change_file"
fi
done
if [ -e "$perm_change_file" ]
then
perms_changed_count=$(wc -l "$perm_change_file" | awk '{print $1}')
echo "$perms_changed_count permission changes were made"
echo "See $perm_change_file for details."
else
echo "No permissions were updated."
fi
echo "Configuration files still owned by the default user:"
find $conf_dir/ -user 1001 -group 1002
echo "Data files still owned by the default user:"
find $data_dir/ -user 1001 -group 1002
echo "(If any were listed, you might want to correct them)"
# Touch local.php to reload cache
if [ -e "$conf_dir/local.php" ]
then
echo
echo "Updating timestamp on local.php to trigger cache reloading"
touch "$conf_dir/local.php"
fi
echo
echo "Finished"
echo "$work_dir was created during the upgrade and can now be deleted."
exit 0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment