相信大家看完了上篇文章(http://gull.iteye.com/blog/1137200),会对自动化编译,部署和测试的过程与实现细节有所了解。那么我们稍微总结下就会发现其实现的实质就是一系列linux shell命令组成,且都是很常见的,如maven, ssh, scp, unzip, cp, mv, delete, kill, sed, awk等.
我们回想下——项目是采用maven2构建的,而我们CI自动化job的阶段也是借鉴maven2中默认的phases,因此我们做了下映射, 如下图:
此时你会有什么样的想法? —— 我们能否扩展maven2的生命周期,将我们不同流程的脚本嵌入到maven2的各个生命周期中,这样就可以直接使用原生的maven2命令来完成CI自动化流程了。
所幸的是maven2已经提供了这样的机制和插件,在实践中我们主要用到了下面这2个插件:
- maven-antrun-plugin: 自定义ant task来完成代码同步(p4 sync),参数替换,打包上传(scp),集成测试(ant-jmeter)等操作
- maven-sshexec-plugin:主要是用来执行远程服务器命令来完成清理和部署(ssh, del, unzip, scp, kill, java)等操作
这2个插件如何操作可以查看相关手册:
http://maven.apache.org/plugins/maven-antrun-plugin/
http://evgeny-goldin.com/wiki/Maven-sshexec-plugin
maven-antrun-plugin这个插件官方介绍得比较简单,远不能达到我们的需要,因此我们需要扩展ant task来完成CI流程的定制化。
如何扩展?我们将以code sync和test为例来介绍。
code sync&test stage
- 实现org.apache.tools.ant.Task接口
public class CodeSync extends Task {
//todo
public void execute() throws BuildException {
//todo prepare some parameters
try {
//sync the application code
sync(depot, p4ClientRoot, p4ClientFolder);
} catch (Exception e) {
e.printStackTrace();
throw new BuildException(e);
}
}
private void sync(String depotRoot, String clientRoot, String folderPath) throws Exception {
String depotPath = depotRoot + "/" + folderPath + "/...";
IServer server = ServerFactory.getServer("p4java://p4host:p4port", null);
server.connect();
server.setCharsetName(p4Charset);
server.setUserName(p4User);
//login the p4 server
server.login(p4Passwd);
IClient client = server.getClient(p4Client);
server.setCurrentClient(client);
List<IFileSpec> fileSpecs = server.getDepotFiles(FileSpecBuilder.makeFileSpecList(new String[] { depotPath }), false);
log("sync "+fileSpecs.size()+" flies");
client.sync(fileSpecs, true, false, false, false);
server.logout();
}
}
- 创建taskdef.properties文件
codesync=com.xxx.ant.tasks.CodeSync
- 配置pom插件
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-antrun-plugin</artifactId> <version>1.6</version> <executions> <execution> <id>initialize</id> <phase>initialize</phase> <configuration> <target> <echo message="**************************"/> <echo message="** A P P S CODE SYNC "/> <echo message="**************************"/> <typedef file="$typedef.properties" /> <codesync> <parameter name="maven">xxx.xxx.xxx</parameter> <parameter name="xxxx">yyyy</parameter> </codesync> <echo message="**************************"/> <echo message="** END CODE SYNC *********"/> <echo message="**************************"/> <echo message=""/> </target> </configuration> <goals> <goal>run</goal> </goals> </execution> <execution> <id>compile-skip-unittest</id> <phase>test</phase> <configuration> <target if="maven.test.skip"> <echo message="*****************************************"/> <echo message="===== BEGIN COMPILE(TEST) ===="/> <echo message="*****************************************"/> <exec dir="/xxx/yyy/sourceFolder" executable="mvn"> <arg line="clean compile -Dmaven.test.skip=true -Ddependency.locations.enabled=false"/> </exec> <echo message= "***************************************"/> <echo message= "===== END COMPILE(TEST) ===="/> <echo message= "***************************************"/> </target> </configuration> <goals> <goal>run</goal> </goals> </execution> <execution> <id>compile-unit-test</id> <phase>test</phase> <configuration> <target unless="maven.test.skip"> <echo message="***********************************"/> <echo message="===== BEGIN COMPILE ===="/> <echo message="***********************************"/> <echo message="//TODO: prepare unittest environment "/> <exec dir="/xxx/yyy/sourceFolder" executable="mvn"> <arg line="clean compile -Ddependency.locations.enabled=false"/> </exec> <echo message="***********************************"/> <echo message="===== BEGIN COMPILE ===="/> <echo message="***********************************"/> </target> </configuration> <goals> <goal>run</goal> </goals> </execution> </executions> <dependencies> <dependency> <groupId>com.xxxx</groupId> <artifactId>ci-anttasks</artifactId> <version>1.0.0</version> </dependency> </dependencies> </plugin> </plugins> </build>
在这个配置文件中,我们扩展了maven2的initialize和test周期,并通过ant来执行我们想要的操作。
Deploy Stage
对于deploy过程也是比较杂的,也需要按照相同的方式来扩展,并且还要配合maven-sshexec-plugin插件来完成部署操作。
pom.xml配置片段
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-antrun-plugin</artifactId> <version>1.6</version> <executions> <execution> <id>upload package</id> <phase>pre-integration-test</phase> <configuration> <target> <mkdir dir="..."/> <!-- upload package to the remote server--> <scp todir="deployserver:path" keyfile="$keyfile" trust="true" failοnerrοr="false"> <fileset dir="$buildFolder/"> <include name="*.zip"/> </fileset> </scp> ... </target> </configuration> <goals> <goal>run</goal> </goals> </execution> </executions> <dependencies> <dependency> <groupId>com.xxxx</groupId> <artifactId>ci-anttasks</artifactId> <version>1.0.0</version> </dependency> </dependencies> </plugin> <plugin> <groupId>com.goldin.plugins</groupId> <artifactId>maven-sshexec-plugin</artifactId> <executions> <!-- Run commands on each remote server --> <execution> <id>do deploy</id> <phase>pre-integration-test</phase> <goals> <goal>sshexec</goal> </goals> <configuration> <location>scp://$deployserver/uploadFolder</location> <keyfile>$keyFile</keyfile> <commands> <command>echo '********************************'</command> <command>echo '==== { Run Remote Commands} ===='</command> <command>echo '********************************'</command> <command> unzip xx.zip </command> <command>echo ''</command> <command>echo '==== END Run Remote Commands ===='</command> <command>echo '*********************************'</command> <command>echo ''</command> </commands> </configuration> </execution> </executions> </plugin>
Integration Test Stage
目前integration test我们主要是采用jmeter来进行完成API测试的,因此我们需要配置ant jmeter task.
<execution> <phase>integration-test</phase> <configuration> <target> <echo message="******************************************"/> <echo message="==== { Run Integration test Scripts } ===="/> <echo message="******************************************"/> <path id="jmeter.classpath"> <fileset dir="$jarFolder"> <include name="ant-jmeter*.jar"/> </fileset> </path> <taskdef name="jmeter" classpathref="jmeter.classpath" classname="org.programmerplanet.ant.taskdefs.jmeter.JMeterTask"/> <property name="transformation.xsl" value="$resourcesFolder/jmeter-results-detail-report.xsl" /> <property name="resourcesFolder" value="$resourcesFolder" /> <ant antfile="$jmeter.integration.testscript" /> <echo message=""/> <echo message="=====END Integration Test====="/> <echo message="******************************"/> </target> </configuration> <goals> <goal>run</goal> </goals> </execution>
到此maven2生命周期的扩展算是结束了,但如果子项目比较多的话,每一个这样配置很是麻烦,可以通过下面2种手段解决:
- 构建maven parent project: 通过继承和参数覆盖的方式简化配置,具体方式可参考maven手册。
- 编写pom生成工具: 通过简单的配置参数和文件模板生成目标pom,如图所示
——全文完