﻿#!/bin/sh
#
#   This program is free software: you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation, either version 3 of the License, or
#   (at your option) any later version.
#
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#   A copy of the GNU General Public License is available at
#   <http://www.gnu.org/licenses/>.
#
#
###########################################################
#           sssync - SilverStripe Site Sync               #
###########################################################
#
# Author: David Harrison
# Date: 9 December 2009
#
# This script synchronises a remote SilverStripe installation
# with a local copy. It is assumed that SilverStripe's caching
# mechanism is enabled.
# For SSH authentication to occur without a password prompt,
# SSH keys should be generated to allow password-less login.
#
# ---------------------------------------------------------

#############################################
# Variables pulled from the supplied config #
#############################################

# Local website directory
localSSDir=`awk '/^Local_SilverStripe_Directory/{print $2}' $1`
# Local temp directory
tempDir=`awk '/^Local_Temp_Directory/{print $2}' $1`
# Local MySQL configuration
localMySQLUser=`awk '/^Local_MySQL_User/{print $2}' $1`
localMySQLPassword=`awk '/^Local_MySQL_Password/{print $2}' $1`
localMySQLHost=`awk '/^Local_MySQL_Host/{print $2}' $1`
localMySQLPort=`awk '/^Local_MySQL_Port/{print $2}' $1`
localMySQLDatabase=`awk '/^Local_MySQL_Database/{print $2}' $1`
localMySQLTempDatabase=`awk '/^Local_MySQL_TempDatabase/{print $2}' $1`

# Remote SSH configuration
remoteSSHUser=`awk '/^Remote_SSH_User/{print $2}' $1` 
remoteSSHHost=`awk '/^Remote_SSH_Host/{print $2}' $1` 
remoteSSHPort=`awk '/^Remote_SSH_Port/{print $2}' $1`
# Remote directories
remoteSSDir=`awk '/^Remote_SilverStripe_Directory/{print $2}' $1`
remoteBackupDir=`awk '/^Remote_Backup_Directory/{print $2}' $1`
# Remote MySQL configuration
remoteMySQLUser=`awk '/^Remote_MySQL_User/{print $2}' $1`
remoteMySQLPassword=`awk '/^Remote_MySQL_Password/{print $2}' $1`
remoteMySQLHost=`awk '/^Remote_MySQL_Host/{print $2}' $1`
remoteMySQLPort=`awk '/^Remote_MySQL_Port/{print $2}' $1`
remoteMySQLDatabase=`awk '/^Remote_MySQL_Database/{print $2}' $1`
# The email options - email requires sendmail or postfix running locally
emailRecipient=`awk '/^Recipient_Email_Address/{print $2}' $1`


# The sendEmail function delivers an email notification.
# It takes the following parameters:
#  1- Subject
#  2- Message
sendEmail() {
   echo "Sending email to $emailRecipient:"
   echo "  Subject - ${1}"
   echo "  Message - ${2}"
   echo ${2} | mail -s "${1}" $emailRecipient
}

# The buildStripVersionsSQL function constructs a temporary SQL file that contains
# commands for removing the revisions from the temp database.
#
# Note: If you have custom page types include the relevant SQL statements below
buildStripVersionsSQL() {
   echo "DELETE FROM ErrorPage_versions;" > ${tempDir}/sssync.sql
   echo "DELETE FROM GhostPage_versions;" >> ${tempDir}/sssync.sql
   echo "DELETE FROM RedirectorPage_versions;" >> ${tempDir}/sssync.sql
   echo "DELETE FROM SiteTree_versions;" >> ${tempDir}/sssync.sql
   echo "DELETE FROM VirtualPage_versions;" >> ${tempDir}/sssync.sql
}

# The cleanTemp function removes the temporary error file.
cleanTemp() {
   rm ${tempDir}/sssync.err 
}

# The rollBackChanges function restores the file and database backup of the remote website.
rollBackChanges() {
   echo "Rolling back the remote file changes"
   ssh ${remoteSSHUser}@${remoteSSHHost} -p ${remoteSSHPort} \
       "tar -xzf ${remoteBackupDir}/html.tgz -C ${remoteSSDir}"
   echo "Rolling back the remote database changes"
   ssh ${remoteSSHUser}@${remoteSSHHost} -p ${remoteSSHPort} \
       "mysql -u ${remoteMySQLUser} -h ${remoteMySQLHost} -p${remoteMySQLPassword} -P ${remoteMySQLPort} \
       ${remoteMySQLDatabase} < ${remoteBackupDir}/backup.sql"
}


echo
echo "__________________________________________"
echo "|  SilverStripe Sync process initiated   |"
echo "‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾"
echo
echo "Local SilverStripe directory: ${localSSDir}"
echo "Temporary directory: ${tempDir}"
echo "------------------------------------------"
echo

echo "Rebuilding the local SilverStripe cache"
cd ${localSSDir}
sapphire/sake dev/buildcache flush=1 > ${tempDir}/sssync.err 2>&1
localCacheRebuilt=`tail ${tempDir}/sssync.err | grep "== Done! =="`
cleanTemp

if [ "${localCacheRebuilt}" != "== Done! ==" ]
then
   echo
   echo "** Error rebuilding the SilverStripe cache - Exiting **"
   echo
   
   sendEmail "Error rebuilding local SilverStripe cache"\
             "There was an error rebuilding the SilverStripe cache. The sync process was not undertaken."
   exit
fi


#############################################
# Create a local, revisionless SS database  #
#############################################

echo "Creating a temporary, revisionless database"

mysqldump -C -u ${localMySQLUser} -p${localMySQLPassword} -h ${localMySQLHost} -P ${localMySQLPort} ${localMySQLDatabase} | \
	mysql -u ${localMySQLUser} -p${localMySQLPassword} -h ${localMySQLHost} -P ${localMySQLPort} ${localMySQLTempDatabase} > ${tempDir}/sssync.err 2>&1

# Create the SQL file to pass to the temp database
buildStripVersionsSQL

# Stip the versions from the temp database
mysql -u ${localMySQLUser} -p${localMySQLPassword} -h ${localMySQLHost} -P ${localMySQLPort} ${localMySQLTempDatabase} \
	< ${tempDir}/sssync.sql > ${tempDir}/sssync.err 2>&1

# Remove the temporary SQL file
rm ${tempDir}/sssync.sql 
	
localDBCreated=$(cat ${tempDir}/sssync.err)
cleanTemp

if [ "${localDBCreated}" != "" ]
then
   echo
   echo "** Error creating a revisionless database - Exiting **"
   echo
   sendEmail "Error creating revisionless database"\
       "There was an error creating a revisionless version of the local database. The sync process was not undertaken."
   exit
fi


#############################################
# Before performing the sync, make a backup #
#############################################

echo "Moving HTML backup ${remoteBackupDir}/html.tgz to ${remoteBackupDir}/html.tgz.old"
ssh ${remoteSSHUser}@${remoteSSHHost} -p ${remoteSSHPort} \
	"mv ${remoteBackupDir}/html.tgz ${remoteBackupDir}/html.tgz.old"
echo "Moving SQL backup ${remoteBackupDir}/backup.sql to ${remoteBackupDir}/backup.sql.old"
ssh ${remoteSSHUser}@${remoteSSHHost} -p ${remoteSSHPort} \
	"mv ${remoteBackupDir}/backup.sql ${remoteBackupDir}/backup.sql.old"

echo "Creating backup of the remote website"
remoteBackupMade=`ssh ${remoteSSHUser}@${remoteSSHHost} -p ${remoteSSHPort} \
	"tar -czf ${remoteBackupDir}/html.tgz -C ${remoteSSDir} ."`

echo "Creating backup of the remote database"
remoteBackupMade="${remoteBackupMade}`ssh ${remoteSSHUser}@${remoteSSHHost} -p ${remoteSSHPort} \
	"mysqldump -u ${remoteMySQLUser} -h ${remoteMySQLHost} -P ${remoteMySQLPort} -p${remoteMySQLPassword} \
	 ${remoteMySQLDatabase} > ${remoteBackupDir}/backup.sql"`"

if [ "${remoteBackupMade}" != "" ]
then
   echo
   echo "** Error creating remote backup - Exiting **"
   echo
   echo "Moving the old remote backups into place"
   ssh ${remoteSSHUser}@${remoteSSHHost} -p ${remoteSSHPort} \
	   "mv ${remoteBackupDir}/html.tgz.old ${remoteBackupDir}/html.tgz"
   ssh ${remoteSSHUser}@${remoteSSHHost} -p ${remoteSSHPort} \
       "mv ${remoteBackupDir}/backup.sql.old ${remoteBackupDir}/backup.sql"
   sendEmail "Error creating remote backup"\
       "There was an error creating a backup of the remote website files or database. The sync process was not undertaken."
   exit
fi

# Variable to hold sync failure flag
syncFailure="false"

##############################################
# Perform the synchronisation of file assets #
##############################################

echo "Synchronising remote website assets/Uploads directory with local copy"
remoteFileSync=`rsync -aqz --delete -e "ssh -p ${remoteSSHPort}" ${localSSDir}/assets/Uploads/ \
	${remoteSSHUser}@${remoteSSHHost}:${remoteSSDir}/assets/Uploads/`

if [ "${remoteFileSync}" != "" ]
then
   syncFailure="true"
   echo
   echo "** Error synchronising website assets/Uploads - Rolling back changes **"
   echo
   
   sendEmail "Error synchronising website assets/Uploads"\
       "There was an error synchronising the remote website's asset directory. The sync process was rolled back."
fi


##############################################
# Synchronise the local and remote databases #
##############################################

echo "Synchronising the remote database with the local (temp) database"

mysqldump -C -u ${localMySQLUser} -p${localMySQLPassword} -h ${localMySQLHost} -P ${localMySQLPort} ${localMySQLTempDatabase} | \
	ssh ${remoteSSHUser}@${remoteSSHHost} -p ${remoteSSHPort} \
	"mysql -u ${remoteMySQLUser} -h ${remoteMySQLHost} -p${remoteMySQLPassword} -P ${remoteMySQLPort} ${remoteMySQLDatabase}" > ${tempDir}/sssync.err 2>&1

remoteMySQLSync=$(cat ${tempDir}/sssync.err)
cleanTemp

if [ "${remoteMySQLSync}" != "" ]
then
   syncFailure="true"
   echo
   echo "** Error synchronising the MySQL databases - Rolling back changes **"
   echo
   
   sendEmail "Error synchronising the MySQL databases"\
       "There was an error synchronising the two MySQL databases. The sync process was rolled back."
fi

if [ "${syncFailure}" == "true" ]
then
   echo
   echo "** Error synchronising the SilverStripe site - Rolling back & exiting **"
   echo
   
   # Roll back the file and database changes
   rollBackChanges
   
   sendEmail "Error synchronising the remote website"\
       "There was an error performing the synchronisation process. The sync process was rolled back."
   exit
fi


##############################################
# Rebuild the remote SilverStripe web cache  #
##############################################

echo "Rebuilding the remote SilverStripe cache"
ssh ${remoteSSHUser}@${remoteSSHHost} -p ${remoteSSHPort} \
	"cd ${remoteSSDir}; sapphire/sake dev/buildcache flush=1" > ${tempDir}/sssync.err 2>&1
remoteCacheRebuilt=`tail ${tempDir}/sssync.err | grep "== Done! =="`
cleanTemp

if [ "${remoteCacheRebuilt}" != "== Done! ==" ]
then
   echo
   echo "** Error rebuilding the remote SilverStripe cache - Rolling back changes **"
   echo
   
   # Roll back the file and database changes
   rollBackChanges
   
   sendEmail "Error rebuilding the remote SilverStripe cache"\
             "There was an error rebuilding the remote SilverStripe cache."
   exit
fi

sendEmail "SilverStripe was successfully synchronised"\
             "Congratulations, the remote website was synchronised without any issues."

echo
echo "__________________________________________"
echo "SilverStripe was successfully synchronised"
echo "=========================================="
echo


