依据前文《通过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--