#!/bin/bash ################################################################################ # THIS SCRIPT USED FOR CHECKOUT/UPDATE REMOTE SITE USING SVN # # # 1. place scripts in save level with site and www folders # 2. update variables in the .remote-update/config.sh # 3. make sure there no site and www/index.php in remote server to make checkout # 4. run this script # 5. you should manually do following tasks: # - setup database dump # - setup cronjobs # - upload userfiles # - upload local.config.php # 6. use the same script to upload changed files to remote server # ################################################################################ if test $(uname -n) != "CatZilla"; then echo "> ERROR: script should be executed on CatZilla" 1>&2 exit 1 fi VERSION="1.0.5" VERBOSE=0 USE_TEST_CONFIG="n" SCRIPT_DIR="$(dirname $0)/.remote-update" if [[ ! -f "$SCRIPT_DIR/remote-template/update.sh" || ! -f "$SCRIPT_DIR/remote-template/checkout.sh" ]] ; then echo "> ERROR: can not found script templates" 1>&2 exit 1 fi # custom exit codes ERR_CODE_NO_CHECKOUT=75 ERR_CODE_EXEC_ERROR=77 ERR_CODE_USER_FILES_EXISTS=78 SVN_HOST="svn.orange35.com:80" SVN_BIND_PORT="8888" SVN_WK_PATH=$(svn info | grep "^URL:" | awk '{print $2}' | cut -d/ -f4-) UPLOAD_USERFILES="n" REMOTE_FORCE_UNPACK="n" if [[ -n $SSH_CLIENT ]] ; then CLIENT="$(whoami) ${SSH_CLIENT%% *}" else CLIENT="$(whoami)" fi LOCK_FILE="/tmp/remote-update.lock" printUsage() { local SCRIPT_NAME=$(basename $0) echo "${SCRIPT_NAME} ${VERSION}" echo "Usage: ${SCRIPT_NAME} [OPTIONS]" echo "Options:" echo " -v, --verbose display revisions and linked issues" echo " --force-pack-all force repack resources" echo " --tunnel create ssh tunnel only (w/o executing any commands)" echo " --upload-userfiles upload userfiles" echo " --test-config use test config .remote-update/testconfig.sh" echo " --help display this message and exit" echo " --version display script version and exit" echo echo "1. place scripts in save level with site and www folders" echo "2. update variables in the .remote-update/config.sh" echo "3. make sure there no site and www/index.php in remote server to make checkout" echo "4. run this script" echo "5. you should manually do following tasks:" echo " - setup database dump" echo " - setup cronjobs" echo " - upload userfiles (if option '--upload-userfiles' was not used)" echo "6. use the same script to upload changed files to remote server" } checkDependencies() { local name for name in "ssh" "scp" "svn" "grep" "awk" "sed" "curl" "xpath" "printf"; do if ! type $name &>/dev/null; then echo "Error: required dependency \"$name\" not found" exit 1 fi done } substVars() { sed -e "s|{{ERR_CODE_NO_CHECKOUT}}|$ERR_CODE_NO_CHECKOUT|g" \ | sed -e "s|{{ERR_CODE_EXEC_ERROR}}|$ERR_CODE_EXEC_ERROR|g" \ | sed -e "s|{{ERR_CODE_USER_FILES_EXISTS}}|$ERR_CODE_USER_FILES_EXISTS|g" \ | sed -e "s|{{REMOTE_HOST}}|$REMOTE_HOST|g" \ | sed -e "s|{{REMOTE_SITE_DIR}}|$REMOTE_SITE_DIR|g" \ | sed -e "s|{{REMOTE_WWW_DIR}}|$REMOTE_WWW_DIR|g" \ | sed -e "s|{{REMOTE_FORCE_UNPACK}}|$REMOTE_FORCE_UNPACK|g" \ | sed -e "s|{{SVN_REPO_PATH}}|$SVN_REPO_PATH|g" \ | sed -e "s|{{SVN_QSF_REPO_PATH}}|$SVN_QSF_REPO_PATH|g" \ | sed -e "s|{{SVN_HOST}}|$SVN_HOST|g" \ | sed -e "s|{{SVN_BIND_PORT}}|$SVN_BIND_PORT|g" \ | sed -e "s|{{CLIENT}}|$CLIENT|g" \ | sed -e "s|{{LOCK_FILE}}|$LOCK_FILE|g" } # usage: inArray "item" "${array[@]}" inArray () { local e for e in "${@:2}"; do [[ "$e" == "$1" ]] && return 0; done return 1 } validate() { if [ "$SVN_REPO_PATH" != "$SVN_WK_PATH" ]; then echo -e "\e[0;38;05;196mERROR:\e[0m The working copy URL path \e[0;38;05;196m\"$SVN_WK_PATH\"\e[0m different from one that is set in the SVN_REPO_PATH=\e[0;38;05;120m\"$SVN_REPO_PATH\"\e[0m." echo -e "Please make sure that the configuration is correct!!!" exit 1; fi } upload() { read -p "> Update project sources from SVN (Y/N)? " -n 1 echo if [[ ! $REPLY =~ ^[Yy]$ ]] ; then exit 1 fi CMD_REMOTE_UPDATE=$(cat $SCRIPT_DIR/remote-template/update.sh | substVars) CMD_REMOTE_CHECKOUT=$(cat $SCRIPT_DIR/remote-template/checkout.sh | substVars) echo "> running remote command ..." ssh -TR ${SVN_BIND_PORT}:${SVN_HOST} ${REMOTE_HOST} "$CMD_REMOTE_UPDATE" CODE=$? if [[ $CODE -eq $ERR_CODE_EXEC_ERROR ]] ; then echo "> error executing command" 1>&2 exit 1 elif [[ $CODE -eq $ERR_CODE_NO_CHECKOUT ]] ; then read -p "> Checkout project sources from SVN (Y/N)? " -n 1 echo if [[ ! $REPLY =~ ^[Yy]$ ]] ; then exit 1 fi ssh -TR ${SVN_BIND_PORT}:${SVN_HOST} ${REMOTE_HOST} "$CMD_REMOTE_CHECKOUT" CODE=$? if [[ $CODE -eq $ERR_CODE_EXEC_ERROR ]] ; then echo "> error executing command" 1>&2 exit 1 fi fi echo "> done" } uploadUserfiles() { CMD_REMOTE=$(cat $SCRIPT_DIR/remote-template/userfiles.sh | substVars) echo "> running remote command ..." ssh -TR ${SVN_BIND_PORT}:${SVN_HOST} ${REMOTE_HOST} "$CMD_REMOTE" CODE=$? if [[ $CODE -eq $ERR_CODE_EXEC_ERROR ]] ; then echo "> error executing command" 1>&2 exit 1 elif [[ $CODE -eq $ERR_CODE_USER_FILES_EXISTS ]] ; then read -p "> userfiles directory already exists. Do you want to unpack archive over it (Y/N)? " -n 1 echo if [[ ! $REPLY =~ ^[Yy]$ ]] ; then return 1 fi REMOTE_FORCE_UNPACK="y" CMD_REMOTE=$(cat $SCRIPT_DIR/remote-template/userfiles.sh | substVars) ssh -TR ${SVN_BIND_PORT}:${SVN_HOST} ${REMOTE_HOST} "$CMD_REMOTE" fi echo "> done" } checkRevisions() { local code local remoteCmd local data local remoteRev local repoRev echo "> checking revisions ..." remoteCmd="cd ${REMOTE_SITE_DIR} && svn info --non-interactive --xml" data=$(ssh -TR ${SVN_BIND_PORT}:${SVN_HOST} ${REMOTE_HOST} "$remoteCmd" 2>/dev/null) code=$? if [[ $code != 0 ]] ; then echo "> error getting remote revision" 1>&2 return fi remoteRev=$(echo $data | xpath -q -e 'string(//info/entry/commit/@revision)' 2>/dev/null) code=$? if [[ $code != 0 ]] ; then echo "> error parsing remote revision info:\n${data}" 1>&2 return fi repoRev=$(svn info --xml --non-interactive http://$SVN_HOST/$SVN_REPO_PATH | xpath -q -e 'string(//info/entry/commit/@revision)' 2>/dev/null) code=$? if [[ $code != 0 ]] ; then echo "> error parsing local revision info" 1>&2 return fi if [[ $remoteRev -ge $repoRev ]] ; then echo "> latest version already uploaded (repository revision: ${repoRev} remote revision: ${remoteRev})" return fi echo "Repository Revision: ${repoRev}" echo "Remote Revision: ${remoteRev}" data=$(svn log --xml -r $remoteRev:HEAD | xpath -q -e '//logentry/msg/text()' 2>/dev/null | grep -oi -P 'issue \w+-\d+' | awk '{print $2}' | sort -u) code=$? if [[ $code != 0 ]] ; then echo "> error getting issue data:\n${data}" 1>&2 return fi echo "Issues:" local issueId local issueData local issueState local issueAssignee local color local reset="\e[0m" for issueId in $data ; do issueData=$(curl --insecure --user svn:postcommit https://youtrack.orange35.com/rest/issue/$issueId 2>/dev/null) code=$? issueState="?" issueAssignee="?" if [[ $code == 0 ]] ; then issueState=$(echo "$issueData" | xpath -q -e '//field[@name="State"]/value[1]/text()' 2>/dev/null) issueAssignee=$(echo "$issueData" | xpath -q -e '//field[@name="Assignee"]/value[1]/text()' 2>/dev/null) fi case $issueState in ( 'Submitted' ) color="\e[38;05;11m" ;; ( 'Open' ) color="\e[38;05;6m" ;; ( 'In Progress' ) color="\e[38;05;4m" ;; ( 'Reopened' ) color="\e[38;05;1m" ;; ( 'Fixed' ) color="\e[38;05;2m" ;; ( 'Verified' ) color="\e[38;05;10m" ;; ( 'Closed' ) color="\e[38;05;8m" ;; esac printf " %-10s ${color}%-14s${reset} Assignee: %s\n" $issueId "[$issueState]" $issueAssignee done } HOSTS_TO_UNLOCK=() remoteLock() { echo "> creating remote lock for host $REMOTE_HOST" local remoteCommand=$(cat $SCRIPT_DIR/remote-template/lock.sh | substVars) ssh -T ${REMOTE_HOST} "$remoteCommand" code=$? if [ $code -ne 0 ] ; then echo "> failed to lock host ${REMOTE_HOST}" 1>&2 return 1 fi return 0 } remoteUnlockAll() { local host local remoteCommand echo echo "> removing remote locks for hosts ${HOSTS_TO_UNLOCK[*]}" for host in ${HOSTS_TO_UNLOCK[*]} ; do remoteCommand=$(cat $SCRIPT_DIR/remote-template/unlock.sh | sed -e "s|{{LOCK_FILE}}|$LOCK_FILE|g") ssh -T ${REMOTE_HOST} "$remoteCommand" code=$? if [ $code -ne 0 ] ; then echo "> failed to unlock host ${host}" 1>&2 fi done return 0 } #---------------------------------------------------------------------------------------------------------------------- # MAIN #---------------------------------------------------------------------------------------------------------------------- checkDependencies for opt in $@ ; do case ${opt} in ( '--help' ) printUsage exit 0 ;; ( '--upload-userfiles' ) shift UPLOAD_USERFILES="y" ;; ( '--test-config' ) shift USE_TEST_CONFIG="y" ;; ( '--version') echo $(basename $0) $VERSION exit 0 ;; esac done if [ $USE_TEST_CONFIG == "y" ] ; then configFiles=( $SCRIPT_DIR/testconfig.sh ) else configFiles=( $SCRIPT_DIR/config* ) fi HOSTS_TO_UNLOCK=() trap 'remoteUnlockAll' EXIT for file in "${configFiles[@]}" do . $file echo "REMOTE_HOST: ${REMOTE_HOST}" echo "REMOTE_SITE_DIR: ${REMOTE_SITE_DIR}" echo "REMOTE_WWW_DIR: ${REMOTE_WWW_DIR}" echo "SVN_REPO_PATH: ${SVN_REPO_PATH}" echo "SVN_QSF_REPO_PATH: ${SVN_QSF_REPO_PATH}" echo validate inArray "$REMOTE_HOST" "${HOSTS_TO_UNLOCK[@]}" || remoteLock code=$? if [ $code -eq 0 ] ; then inArray "$REMOTE_HOST" "${HOSTS_TO_UNLOCK[@]}" || HOSTS_TO_UNLOCK+=($REMOTE_HOST) for opt in $@ ; do case ${opt} in ( '-v' | '--verbose' ) shift VERBOSE=1 checkRevisions ;; ( '--force-pack-all' ) shift read -p "> Do you want to repack all js/css files (Y/N)? " -n 1 echo if [[ ! $REPLY =~ ^[Yy]$ ]] ; then exit 1 fi REMOTE_CMD=$(cat $SCRIPT_DIR/remote-template/pack.sh | substVars) echo "> running remote command ..." ssh -TR ${SVN_BIND_PORT}:${SVN_HOST} ${REMOTE_HOST} "$REMOTE_CMD" CODE=$? if [[ $CODE -eq $ERR_CODE_EXEC_ERROR ]] ; then echo "> error executing command" 1>&2 exit 1 fi echo "> done" exit ;; ( '--tunnel' ) ssh -R ${SVN_BIND_PORT}:${SVN_HOST} ${REMOTE_HOST} exit ;; esac done if [ "$UPLOAD_USERFILES" == "y" ] ; then read -p "> Do you upload userfiles to server (Y/N)? " -n 1 echo if [[ $REPLY =~ ^[Yy]$ ]] ; then userfilesArchive="uf.tar.bz2" echo "Creating $userfilesArchive ..."; tar -C www/userfiles -cjf $userfilesArchive . echo "Uploading $userfilesArchive to remote server ..."; scp $userfilesArchive "$REMOTE_HOST:$REMOTE_WWW_DIR" > /dev/null 2>&1 uploadUserfiles rm $userfilesArchive fi fi upload if [ -f "site/bin/hart" ] ; then scp site/bin/hart "$REMOTE_HOST:$REMOTE_SITE_DIR/bin/hart" > /dev/null 2>&1 fi fi done exit 0