Last active
September 3, 2015 08:22
-
-
Save luceos/4dc6b2b97c34b87cf9f3 to your computer and use it in GitHub Desktop.
update script for laravel 4/5 creating a backup of files and database, while asking for approval along the way
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# | |
# Laravel update tool | |
# | |
# | |
# use: ./rollout.sh | |
# | |
# date: 2015-09-01 | |
# by D. Klabbers / HostingXS B.V. | |
# for: Laravel 4.* and 5.* (with modification of DATABASE_CONFIG_PATH) | |
# license: MIT | |
# disclaimer: | |
# Use at your own risk, run on a separate install to understand what it does. | |
# Restating the license; we are not liable for any damages done! | |
# | |
####################################################################### | |
# | |
# One ring to rule them all, global defines to vassalage | |
# | |
####################################################################### | |
# debug allows the use of writeDebug in the code, if 0 output of that function will be disabled | |
# this is not used anywhere unless we're debugging code | |
DEBUG=1 | |
# set this to 1 to enable auto incrementing vendor package versions | |
UPDATE_VERSION=1 | |
# use ./rollout.sh 5 to use this script for laravel 5 | |
LARAVEL_VERSION=4 | |
if [ -n "${BASH_ARGV[0]}" ] && [ ${BASH_ARGV[0]} == 5 ]; then | |
LARAVEL_VERSION=5 | |
fi | |
# path to the database configuration file, we will parse this file to read the connection settings | |
# in case of laravel version 5.* use 'config/database.php' | |
DATABASE_CONFIG_PATH='app/config/database.php' | |
if [ ${LARAVEL_VERSION} == 5 ]; then | |
DATABASE_CONFIG_PATH='config/database.php' | |
fi | |
# the key in the database connections array from which we parse credentials | |
DATABASE_CONNECTION='mysql' | |
# specify the vendor folder, this checks for non-committed and modified files; eg: vendor/my-self | |
# consider to update this to the workbench directory or set to empty string to skip check | |
VENDOR_FOLDER='' | |
# the following allow checking whether uncommitted or modified files exist in the vendor folder | |
MATCH_GIT_UNCOMMITTED='??' | |
MATCH_GIT_MODIFIED='M' | |
# matcher for http://semver.org compatible versioning | |
REG_SEMVER='^[0-9]+\.[0-9]+\.[0-9]+(\-[a-z0-9\.]+)?$' | |
# sets current directory, so we can get back to where we came from | |
CURRENT_PATH=${PWD} | |
CURRENT_MACHINE=$(hostname) | |
####################################################################### | |
# | |
# Formatting funkiness favorable fathomness | |
# | |
####################################################################### | |
# colors to be used for displaying messages while running the script | |
COLOR_DEFAULT='\033[0m' | |
COLOR_YELLOW='\033[1;33m' | |
COLOR_RED='\033[0;31m' | |
COLOR_GREEN='\033[0;32m' | |
COLOR_LIGHT_GRAY='\033[0;37m' | |
function writeLn () | |
{ | |
local line="$1" | |
local color="$2" | |
local appendNewLines="$3" | |
if [ -z ${color} ]; then | |
color=${COLOR_DEFAULT} | |
fi | |
if [ -z ${appendNewLines} ]; then | |
appendNewLines=1 | |
fi | |
printf "${color}${line}${COLOR_DEFAULT}" | |
while [ ${appendNewLines} -gt 0 ] | |
do | |
printf "\n" | |
appendNewLines=$[$appendNewLines-1] | |
done | |
} | |
function writeQuestion () | |
{ | |
writeLn "$1" ${COLOR_YELLOW} | |
} | |
function writeError () | |
{ | |
writeLn "$1" ${COLOR_RED} | |
} | |
function writeInfo () | |
{ | |
writeLn "$1" ${COLOR_LIGHT_GRAY} | |
} | |
function writeHeader () | |
{ | |
writeLn "$1" ${COLOR_GREEN} | |
} | |
function writeDebug () | |
{ | |
if [ ${DEBUG} -eq 1 ]; then | |
writeInfo "DEBUG: $1" | |
fi | |
} | |
# checks status of folder to see whether open / uncommitted changes exist | |
function gitStatus () | |
{ | |
PACKAGE_PATH=${PWD} | |
local clean=0 | |
local tmp=$(git status --porcelain) | |
local line=($tmp) | |
if [ -z "${line[0]}" ]; then | |
clean=$[$clean+1] | |
elif [ "${line[0]}" == "${MATCH_GIT_UNCOMMITTED}" ]; then | |
writeError "${PACKAGE_PATH} has uncommitted files" | |
elif [ "${line[0]}" == "${MATCH_GIT_MODIFIED}" ]; then | |
writeError "${PACKAGE_PATH} has modified files" | |
else | |
writeError "${PACKAGE_PATH} unknown state ${line}" | |
exit 303 | |
fi | |
} | |
####################################################################### | |
# | |
# Find as much as possible before asking stupid questions | |
# | |
####################################################################### | |
CURRENTUSER=$(whoami) | |
####################################################################### | |
# | |
# Shoot first, ask questions later | |
# | |
####################################################################### | |
writeHeader 'HostingXS B.V. update tool (http://www.hostingxs.nl)' | |
writeHeader 'This script will update a Laravel 4.* or 5.* to a new version automatically.' | |
# read the installation docroot | |
writeQuestion 'Please specify the path of the docroot or leave empty for the current directory:' | |
read DOCROOT | |
if [ -z "$DOCROOT" ]; then | |
DOCROOT=${PWD} | |
else | |
DOCROOT="${PWD}/$2" | |
fi | |
# load database information | |
tmp=$(php <<CODE | |
<?php | |
\$config=include('${DOCROOT}/${DATABASE_CONFIG_PATH}'); | |
echo trim(implode(' ', \$config['connections']['${DATABASE_CONNECTION}'])); | |
CODE | |
) | |
DBCONFIG=($tmp) | |
DB_DRIVER=${DBCONFIG[0]} | |
DB_HOSTNAME=${DBCONFIG[1]} | |
DB_DATABASE=${DBCONFIG[2]} | |
DB_USERNAME=${DBCONFIG[3]} | |
DB_PASSWORD=${DBCONFIG[4]} | |
cd ${DOCROOT} | |
# find repository remote url and name (origin) | |
tmp=$(git remote -v | grep fetch) | |
tmpArray=(${tmp}) | |
GIT_REMOTE_NAME=${tmpArray[0]} | |
GIT_REMOTE_URL=${tmpArray[1]} | |
# load latest git tag from the remote | |
tmp=$(git ls-remote --tags ${GIT_REMOTE_URL} | tail -n 1) | |
tmpArray=(${tmp}) | |
GIT_LATEST_TAG=${tmpArray[1]:10} | |
# find the current branch | |
tmp=$(git branch --no-color --contains HEAD) | |
GIT_BRANCH=${tmp:2} | |
cd ${CURRENT_PATH} | |
# read the version | |
if [ -z "${GIT_LATEST_TAG}" ]; then | |
writeQuestion 'Please specify the version to upgrade to, please adhere to http://semver.org standards:' | |
else | |
writeQuestion "Please specify the version to upgrade to, this has be a higher version than (>) ${GIT_LATEST_TAG}:" | |
fi | |
read VERSION | |
if [ -z "$VERSION" ]; then | |
writeError 'Please specify a version to upgrade to.' | |
exit 101 | |
fi | |
if ! [[ "${VERSION}" =~ ${REG_SEMVER} ]]; then | |
writeError 'The specified version does not adhere to the http://semver.org standards.' | |
exit 102 | |
fi | |
# read the user the installation runs under | |
writeQuestion 'Please specify the user the installation runs as or keep blank for "developer":' | |
read RUNSAS | |
if [ -z "$RUNSAS" ]; then | |
RUNSAS='developer' | |
fi | |
# set backup directory | |
BACKUP_DIRECTORY="/home/$RUNSAS/backups/$VERSION" | |
# set backup location for files | |
BACKUP_FILES="${BACKUP_DIRECTORY}/files.tar.gz" | |
writeQuestion "Do you want to pull the latest changes from a branch, before updating? Type 'y' for ${GIT_BRANCH}, type a branch or leave empty to skip:" | |
read BRANCH | |
if [ "${BRANCH}" != 'y' ] && ! [ -z "${BRANCH}" ]; then | |
GIT_BRANCH="${BRANCH}" | |
elif [ -z "$BRANCH" ]; then | |
GIT_BRANCH="" | |
fi | |
####################################################################### | |
# | |
# Check whether installation is safe to update | |
# | |
####################################################################### | |
# run the git status check on the docroot | |
cd ${DOCROOT} | |
gitStatus | |
# run the git status checks on the vendor folder [optional] | |
if [ -n "${VENDOR_FOLDER}" ]; then | |
cd ${VENDOR_FOLDER} | |
for package in `find . -maxdepth 1 -type d` | |
do | |
VENDOR_PATH=${PWD} | |
if [ ${package} != '.' ]; then | |
cd ${package} | |
gitStatus | |
cd ${VENDOR_PATH} | |
fi | |
done | |
fi | |
cd ${CURRENT_PATH} | |
####################################################################### | |
# | |
# List all gathered intel, ask green light for operation "zero day" | |
# | |
####################################################################### | |
writeInfo "Hello ${CURRENTUSER}, your update will concern the following:" | |
writeInfo " - directory:\t\t${DOCROOT}" | |
writeInfo " - update to version:\t${VERSION}" | |
writeInfo " - site user:\t\t${RUNSAS}" | |
writeInfo " - database:\t\t${DB_DATABASE} ${DB_USERNAME}@${DB_HOSTNAME}" | |
writeInfo " - backup directory:\t${BACKUP_DIRECTORY}" | |
if [ -n "${GIT_BRANCH}" ]; then | |
writeInfo " - pulling from branch:\t${GIT_BRANCH}" | |
else | |
writeInfo " - pulling from branch:\tnone" | |
fi | |
writeQuestion 'Please confirm the above information by typing the version:' | |
read ACCEPT | |
if [ ${ACCEPT} != ${VERSION} ]; then | |
writeError 'Process halted, confirmation failed.' | |
exit 666 | |
fi | |
####################################################################### | |
# | |
# Put Laravel in maintenance mode | |
# | |
####################################################################### | |
php ${DOCROOT}/artisan down | |
####################################################################### | |
# | |
# Stuff goes wrong, back up the files | |
# | |
####################################################################### | |
# create a directory for backup | |
writeInfo "Creating backup of previous version in ${BACKUP_DIRECTORY}" | |
if [ ! -d "$BACKUP_DIRECTORY" ]; then | |
mkdir -p ${BACKUP_DIRECTORY} | |
fi | |
# create the backup | |
#tar -cz ${DOCROOT} > "${BACKUP_FILES}" | |
####################################################################### | |
# | |
# Stuff still goes wrong, very much so, safely backup database | |
# | |
####################################################################### | |
BACKUP_DB_FILE="${BACKUP_DIRECTORY}/${DB_DATABASE}.sql" | |
writeInfo "Creating a backup of database ${DB_DATABASE} to ${BACKUP_DB_FILE}" | |
if [ ${DB_DRIVER} == 'pgsql' ]; then | |
PGPASSWORD="${DB_PASSWORD}" | |
# pg_dump --format=p --clean --no-owner --host=${DB_HOSTNAME} --username=${DB_USERNAME} ${DB_DATABASE} > ${BACKUP_DB_FILE} | |
elif [ ${DB_DRIVER} == 'mysql' ]; then | |
mysqldump --quote-names --user=${DB_USERNAME} --password=${DB_PASSWORD} --insert-ignore --skip-comments --add-drop-database ${DB_DATABASE} > ${BACKUP_DB_FILE} | |
else | |
writeError "${DB_DRIVER} is not supported" | |
exit 401 | |
fi | |
####################################################################### | |
# | |
# Check backup before updating | |
# | |
####################################################################### | |
if [ -f ${BACKUP_DB_FILE} ] && [ -f ${BACKUP_FILES} ]; then | |
writeInfo "Backups created succesfully, updating the application." | |
else | |
writeError "Either database dump or files backup hasn't been created." | |
fi | |
####################################################################### | |
# | |
# Start upgrade procedure | |
# | |
####################################################################### | |
cd ${DOCROOT} | |
# let's do a dryrun and then run a pull | |
if [ -n "${GIT_BRANCH}" ]; then | |
git fetch ${GIT_REMOTE_NAME} ${GIT_BRANCH} | |
git diff HEAD..${GIT_REMOTE_NAME}/${GIT_BRANCH} | |
writeQuestion "The above changes will be applied, are you sure? [y/n/s]" | |
read AGREE | |
if [ -z "${AGREE}" ] || [ ${AGREE} == 'n' ]; then | |
writeError "Halting updating." | |
exit 104 | |
elif [ ${AGREE} == 'y' ]; then | |
git merge ${GIT_REMOTE_NAME}/${GIT_BRANCH} | |
elif [ ${AGREE} == 's' ]; then | |
writeInfo "Skipping merge/pull of ${GIT_REMOTE_NAME}/${GIT_BRANCH}" | |
fi | |
fi | |
# check the updates that will be run | |
composer update --dry-run --no-scripts | |
writeQuestion "Are you sure the above dependancies can be safely deployed? [y/n]" | |
read AGREE | |
if [ -z "${AGREE}" ] || [ ${AGREE} == 'n' ]; then | |
writeError "Halting updating." | |
exit 105 | |
elif [ ${AGREE} == 'y' ]; then | |
# update the actual installation | |
composer update | |
if [ ${UPDATE_VERSION} == 1 ]; then | |
# set a new tag | |
git tag -a "${VERSION}" -m "${CURRENTUSER}@${CURRENT_MACHINE}: increment to ${VERSION} from ${GIT_LATEST_TAG}" | |
# commit this tag | |
git push --tags | |
fi | |
fi | |
####################################################################### | |
# | |
# Remove Laravel from maintenance mode | |
# | |
####################################################################### | |
php ${DOCROOT}/artisan up |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment