接上篇文章http://gull.iteye.com/admin/blogs/1136670 讲述自动化Job(编译,部署和测试)的具体实现。
单一项目
单一项目的自动化流程如下图:
看上去流程简单,那么我们CI shell脚本的目录结构也比较清晰,如下:
- preinst: 此文件夹存放的主要是预配置文件或者是服务器控制脚本(如,tomcat的启停脚本)
- scripts: 此文件夹下是各个阶段的CI shell脚本
- process.sh:调用入口,负责参数解析和执行scripts下不同阶段的脚本
- job.conf:是自动化Job的配置文件,提供相关的配置参数,如源码路径,服务器参数,部署环境的数据库连接/用户名/密码等,这些参数将会被scripts下不同阶段的脚本所引用。
命令示例:
- process.sh build 将执行build
- process.sh deploy 将执行build和deploy
- process.sh test 将执行build, deploy和test
- process.sh test -Ddeploy.skip=true 则仅仅执行test
多项目
如果是一个自动化Job中包含多个项目,那么流程和结构则需要稍作调整,如下所示:
- 调用流程
- 文件结构:
process.sh此时需要解析额外的项目参数,如:
- -p project1 表示当前job只针对project 1
- -p project1 project2 表示当前job需要处理project 1 和project 2
存在多分支
同一个项目的多个分支,它们的CI脚本不尽相同,因此每个分支还是应该有自己独立的CI脚本。那么此时的目录结构又会是怎样的呢?
思考下这样合理么?
若是存在许多个release branch,那么脚本目录结构也将比较雍肿,维护起来麻烦。考虑到project 1 的release分支自身就存在独立的目录结构,可将CI脚本分散到项目各自的分支中去:
这样将会达到以下几个目的:
- 源码和脚本捆绑发布,源码的维护和CI脚本的维护视为一个整体操作
- CI脚本中流程控制部分与具体的项目逻辑分离,独立维护和更新
太多重复
CI脚本中充斥着大量重复的代码,因此需要对其进行抽象和提炼,以精简代码提高复用度。
- 代码同步脚本:一个项目的各个子项目或模块通常情况下所用的版本控制器都是相同的,且常用的也是有限的,如svn, perforce等,可以针对不同的服务器提供通用的代码同步的API或者脚本函数
- compile,unittest,package等脚本:对于maven类型的项目来说,基本都是通过maven命令来完成的,所以脚本也相同(除了初始化unittest所依赖环境的脚本)
- deploy 脚本:可以大致分为几类,如tomcat war类型的,java application 类型的等,可以提供几种通用实现。值得注意的是所以依赖的环境基本上都不会一致的,如数据库信息(库名)等。
- integration test脚本:当前主要是通过jmeter来完成自动化测试的,所以脚本也基本一样,当然jmeter参数是不一样的。
- upload package: 主要完成上传包和changlist信息,主要是通过maven 的deploy命令完成,所以也是共用的。
- 命令行参数/文件参数解析脚本
在CI shell编程中,可以将这些公共函数做成库,供不同分支的CI脚本调用。
参考命令
- 获取命令行参数
while getopts ":hs:f:c:" optname do case "$optname" in "h") usage exit 0 ;; "s") log_info "Option $optname[stage] has value $OPTARG" echo $OPTARG //todo ;; "f") log_info "Option $optname[feature(s)] has value $OPTARG" echo $OPTARG //todo ;; "c") log_info "Option $optname[conf] has value $OPTARG" echo $OPTARG //todo ;; "?") log_info "Unknown option $OPTARG" ;; ":") log_info "No argument value for option $OPTARG" ;; esac done
- 获取当前脚本的执行路径下job.conf文件路径
path=`echo $0 | grep "^/"` if test "${path}"; then export conf_file=`dirname $0`/job.conf else export conf_file=`pwd`/job.conf fi
- 判断参数是否为空
if [ -z $stage_parameter ] ; then //todo fi
- 判断文件是否存在
if [ ! -f $conf_file ]; then log_error "CI configuration file doesn't exist, please have a check..." exit -1 fi
- 判断文件目录是否存在,若不存在则创建(若上级目录不存,则递归创建上级目录)
if [ ! -d $WORK_DIRECTORY ]; then //todo mkdir -p $WORK_DIRECTORY fi
- 按","分割参数
for para in `echo $parameters | sed "s/,/ /g"` do //todo done
- 杀掉进程
kill -9 `ps -ef | grep appname | grep -v grep | grep -v kill_appname | awk '{print $2}'`
- 查找替换
VALUEA=$(grep PARAMETERA $conf_file|awk -F= '{print $2}')
sed -i "s/%PARAMETERA%/$VALUEA/g" $target_file
- 发送邮件
cat $mail_file | mutt -F muttrc -s "title" $mail_list
其中muttrc的内容参考如下:
set use_from=yes set from=admin@admin.com set realname=admin@admin.com set editor=vim
- 登录perforce
p4 login <passwd #passwd为密码文本文件
- 获取perforce上APP的最新chang list number
p4 changes -m1 //root/APP_DIRECTORY/... >$latest_changenumber
- 获取perforce上APP changelist number为n1和n2的changelist文件
p4 changes -t -l -s submitted //root/$APP_DIRECTORY/...@$n1,@$n2 >$changelist.txt
- 获取perforce上APP changelist number为n2的最新代码
p4 sync -f //root/$APP_DIRECTORY/...@$n2 |2>&1
更多perforce的命令可以参考官方文档:http://www.perforce.com/perforce/doc.current/manuals/p4v-gs/index.html