依据前文《通过Git WebHooks+脚本实现自动更新发布代码》的解决方案编写的shell脚本,此脚本专门用于更新补丁文件,例如对项目中的文件实现增(add)、删(remove)、改(update),并且执行相关的命令,如清除缓存、重启服务等。

  此Shell脚本目前设计成在本地执行,目前不适合分布式执行的情况。也就是说,此脚本最好与项目在同一个机器上,这个缺陷已经标注在脚本中了,参见脚本中的多个TODO。

  脚本完成的工作:

  • 检查配置文件合规性

  • 备份与恢复

  • 增删改文件

  • 执行命令

  • 失败回滚

  除上述完成的功能外,因为不同的项目其用到的命令或所需要执行的操作以及检查成功与否大有不同,因此其他的功能需要可以继续往上添加,需要的可以自行修改此脚本。此脚本仅作参考之用,欢迎提出改进意见、批评指正。

  此脚本可以略做修改,与前文《通过Git WebHooks+脚本实现自动更新发布代码之shell脚本》提到的部署脚本一起联用。后期改进请关注github。

  脚本如下:

#!/usr/bin/env bash

# Public header
# =============================================================================================================================

# Check that we are root ... so non-root users stop here
[  `id -u` -eq  "0" ] ||  exit 4

# resolve links - $0 may be a symbolic link
PRG="$0"

while [ -h "$PRG" ]; do
  ls=`ls -ld "$PRG"`
  link=`expr "$ls" : '.*-> \(.*\)$'`
  if expr "$link" : '/.*' > /dev/null; then
    PRG="$link"
  else
    PRG=`dirname "$PRG"`/"$link"
  fi
done

# Get standard environment variables
PRGDIR=`dirname "$PRG"`


# echo color function
function cecho {
    # Usage:
    # cecho -red sometext     #Error, Failed
    # cecho -green sometext   # Success
    # cecho -yellow sometext  # Warning
    # cecho -blue sometext    # Debug
    # cecho -white sometext   # info
    # cecho -n                # new line
    # end

    while [ "$1" ]; do
        case "$1" in
            -normal)        color="\033[00m" ;;
# -black)         color="\033[30;01m" ;;
-red)           color="\033[31;01m" ;;
-green)         color="\033[32;01m" ;;
-yellow)        color="\033[33;01m" ;;
-blue)          color="\033[34;01m" ;;
# -magenta)       color="\033[35;01m" ;;
# -cyan)          color="\033[36;01m" ;;
-white)         color="\033[37;01m" ;;
-n)             one_line=1;   shift ; continue ;;
*)              echo -n "$1"; shift ; continue ;;
esac

shift
echo -en "$color"
echo -en "$1"
echo -en "\033[00m"
shift

done
if [ ! $one_line ]; then
        echo
fi
}
# end echo color function

# echo color function, smarter
function echo_r () {
    #Error, Failed
    [ $# -ne 1 ] && return 0
    echo -e "\033[31m$1\033[0m"
}
function echo_g () {
    # Success
    [ $# -ne 1 ] && return 0
    echo -e "\033[32m$1\033[0m"
}
function echo_y () {
    # Warning
    [ $# -ne 1 ] && return 0
    echo -e "\033[33m$1\033[0m"
}
function echo_b () {\
    # Debug
    [ $# -ne 1 ] && return 0
    echo -e "\033[34m$1\033[0m"
}
# end echo color function, smarter

WORKDIR=$PRGDIR
# end public header
# =============================================================================================================================

# begin customization for special case
# project directory to waiting for update
config_project_dir=example_projects
# resources directory which contain config file and update files
config_resources_dir=example_resources
config_config_file=$config_resources_dir/config_update.conf
config_backup_dir=example_backup_dir
# log options
config_this_logfile=$WORKDIR/.update_backup.log
# end

function check_dependencies(){
    echo_b "Checking dependencies for update procedure. "

    if [ -z $config_project_dir ]; then
        echo_r "Error: config_project_dir is undefined! "
        exit 1
    fi

    if [ ! -d $config_resources_dir ]; then
        echo_r "Error: config_resources_dir is undefined! "
    fi

    if [ -z $config_config_file ]; then
        echo_r "Error: config_config_file is undefined! "
        exit 1
    fi

    left_disk_space=`df $config_backup_dir | tail -n1 | awk '{print $(NF -2)}'`
    # set 2097152 to project directory size
    if [ -z $config_project_dir -o ! -d $config_project_dir ]; then
        project_file_space_usage=$(du -s /root | awk '{print $1}')
        required_size=$(expr $project_file_space_usage \* 2)
    fi
    if [[ $left_disk_space -lt $required_size ]]; then
        echo_r "Disk space of $config_backup_dir is smaller than $required_size. "
        exit 1
    fi

    echo_g "All required dependencies check pass! "

}

function test_self(){
    # How to use this function:
    # First execute "$0 test_self", then execute "$0 update"

    echo_b "Test purpose begin. "

    # clean old test example
    echo_b "Clean old test example. "
    [ -d $WORKDIR/example_projects ] && rm -rf $WORKDIR/example_projects
    [ -d $WORKDIR/example_resources ] && rm -rf $WORKDIR/example_resources
    [ -d $WORKDIR/example_backup_dir ] && rm -rf $WORKDIR/example_backup_dir

    # make an example project directory
    if [ -z $config_project_dir -o ! -d $config_project_dir ]; then
        echo_b "Making an example project directory. "
        mkdir $WORKDIR/example_projects
        config_project_dir=example_projects
        # Padding example_projects directory
        touch $config_project_dir/example_filename
        mkdir $config_project_dir/example_directory
    fi

    # make an example resources directory
    if [ -z $config_resources_dir -o ! -d $config_resources_dir ]; then
        echo_b "Making an example resources directory. "
        mkdir  $WORKDIR/example_resources
        config_resources_dir=$WORKDIR/example_resources
    fi

    # make an example config_update.conf
    if [ -z $config_config_file -o ! -f $config_config_file ]; then
        echo_b "Making an example config_update.conf file. "
        touch $config_resources_dir/config_update.conf
        config_config_file=$config_resources_dir/config_update.conf
    # Padding config_update.conf file
    cat >$config_config_file </dev/null 2>&1 ; then
            # do_cp
            do_cp $names
        elif grep $names $config_config_file | grep update >/dev/null 2>&1 ;then
            # do_cp
            do_cp $names
        elif grep $names $config_config_file | grep remove >/dev/null 2>&1 ;then
            # do_remove
            do_remove $names
        else
            exit 1
        fi
    done
    echo_g "Files operations finished successfully! "
}

# TODO
# no example here, please refer to your real production environment
#function do_clean_cache(){}
#function do_restart_service(){}

function service_operation(){
    echo_b "Begin services operations"
    configs=`awk -F '[ ]+' '/^config/ { print $2 }' $config_config_file`
    for names in $configs; do
        if grep $names $config_config_file | grep cleancache | grep enable >/dev/null 2>&1 ; then
            # do_clean_cache
            echo do_clean_cache $names
        elif grep $names $config_config_file | grep cleancache | grep disable >/dev/null 2>&1 ; then
            # echo a warning
            echo_y "Warning: disable action is NOT recommended, $names skipped."
        elif grep $names $config_config_file | grep restartservice | grep enable >/dev/null 2>&1 ; then
            # do_restart_service
            echo do_restart_service $names
        elif grep $names $config_config_file | grep restartservice | grep disable >/dev/null 2>&1 ; then
            # echo a warning
            echo_y "Warning: disable action is NOT recommended, $names skipped."
        else
            echo $names
            echo_r "Error: Wrong config file $config_config_file, please check it. "
            exit 1
        fi
    done
    echo_g "Services operations finished successfully! "
}

function check_remote_server_status(){
    # TODO
    # for remote call
    echo

}

function backup(){
    echo_b "Backup files before update"
#    backup_filename=backup_$(date +%F_%H_%M_%S).tgz
    backup_filename=backup_$(date +%Y_%m_%d_%H_%M_%S).tgz
    tar --create --gzip --absolute-names --file=$config_backup_dir/$backup_filename $config_project_dir
    if [ $? -eq 0 ]; then
        echo_g "Backup files before update finished and successfully! "
        echo "restore_least_file=$config_backup_dir/$backup_filename" > $config_this_logfile
    else
        echo_r "Error: Backup files before update failed! Please alter to administrator. "
        exit 1
    fi

}

function restore(){
    echo_b "Restore files for rollback"
    if [ -f $config_this_logfile ]; then
        . $config_this_logfile
    fi
    restore_least_file=${restore_least_file:-1}
    if [ -s $restore_least_file ]; then
        tar -C $config_project_dir/.. -zxf $restore_least_file
        if [ $? -eq 0 ]; then
            echo_g "Restore files finished and successfully! "
        else
            echo_r "Restore files failed! Please alter to administrator. "
            exit 1
        fi
    else
        echo_r "Can NOT find backup files in $config_backup_dir, backup once indeed? "
        exit 1
    fi

}

# TODO
# for remote call
# function remote_backup(){}
# function remote_restore(){}


function rollback(){
    echo_b "rollback after update failed"
    $0 restore

    echo_g "rollback finished and successfully! "
}

function update_status(){
    # TODO
    # no example here, please refer to your real production environment
    # check if update success or failure
    echo update_status
    # if failure, do rollback action
        # service_operation
}

function update(){
    # TODO
    # thinking carefully with all exit status, which is not good for automatic update 
    check_dependencies
    backup
    file_operation
    service_operation
    update_status
}

function destroy() {
    # echo a warning message
    echo_y "Warning: This action will destroy all this project, and this is unrecoverable! "
    answer="n"
    echo_y "Do you want to destroy this project? "
    read -p "(Default no,if you want please input: y ,if not please press the enter button):" answer
    case "$answer" in
        y|Y|Yes|YES|yes|yES|yEs|YeS|yeS )
        # delete all file expect for this script self
        # find: warning: Unix filenames usually don't contain slashes (though pathnames do).  That means that '-name `./deploy.sh'' will probably evaluate to false all the time on this system.  You might find the '-wholename' test more useful, or perhaps '-samefile'.  Alternatively, if you are using GNU grep, you could use 'find ... -print0 | grep -FzZ `./deploy.sh''.
            # echo $WORKDIR/
            #find -L $WORKDIR -type f ! -name "$(basename $0)" -exec ls --color=auto -al {} \;
            # find -L . -type f ! -name "deploy.sh" -exec ls --color=auto -al {} \;
            # find -L . -type d -exec ls --color=auto -al {} \;
            # find -L ./ -maxdepth 1 ! -name "deploy.sh" ! -wholename "./"
        # ls | grep -v "fielname" |xargs rm -rf
        find -L $WORKDIR -maxdepth 1 ! -name "$(basename $0)" ! -wholename "$WORKDIR"  -exec rm -rf {} \;
        if [ $? -eq 0 ];then
            echo_g "Destory this project successfully! Now will exit with status 0. "
            exit 0
        else
            echo_r "Error: something go wrong! Please check or alter to Admin user! "
            exit 1
        fi
        ;;
        n|N|No|NO|no|nO)
        echo_g "destroy action is cancel"
        exit 0
        ;;
        *)
        echo_r "Are you kidding me? You are a bad kid! "
        exit 1
        ;;
    esac

}

case $1 in
    update)
        update
        ;;
    backup)
        backup
        ;;
    restore)
        restore
        ;;
    rollback)
        rollback
        ;;
    destroy)
        destroy
        ;;
    help|*)
        echo "Usage: $0 {update|backup|restore|rollback|destroy} with $0 itself"
        exit 1
        ;;
esac

# This is not essential with 'case .. esac' handled no args excutions
# replace "exit 0" with ":"
#exit 0
:

tag: 自动更新shell脚本,自动化部署脚本,Shell部署脚本,脚本实现自动更新和回滚,自动化部署shell脚本实例

--end--