浅谈CruiseControl的部署

ANT是用过的最好的Build工具.CruiseControl则通过不断检查SCM (VSS, ClearCase, StarTeam, etc), 一旦发现改动, 调用ANT进行编译, 部署, 实现即时的集成. 一旦编译失败, 则立刻自动发Email给"始作俑"者, 提醒其修正代码. CC自带了一个Web应用, 可是随时查看编译状况和历史状况(包括自从前一次Build, 有哪些文件,是谁做了改动, Build失败的原因, Build的成果(自定义的, 比如最后的打包文件, 自动生成的JavaDoc) 总之, 通过集成Ant和CC, 尽量避免了Daily Build容易Broken的缺点, 而且自动化程度更高. 另外Ant和CC都分别有.net版本, 在下一个.net项目中, 还打算使用它们来进行集成控制.
安装CruiseControl
和AntHill一样,使用CruiseControl构建持续集成系统,需要Tomcat,Ant,CVSNT和WinCVS的支持(参见AntHill:构建Nightly Build系统)。安装CruiseControl很简单,下载cruisecontrol-2.2.zip(参见持续集成资源),解压到安装目录C:\BuildServer,即可完成安装。
安装Tomcat4.1到C:\BuildServer目录,并在C:\BuildServer目录创建workingCopy子目录,作为CruiseControl的工作目录。BuildServer的目录结构如下图所示,其中CruiseControl的启动脚本cruisecontrol.bat(或cruisecontrol.sh)位于BuildServer\cruisecontrol-2.2\main\bin目录下。
准备构建目录结构
1.创建CruiseControl的日志目录:C:\BuildServer\cruisecontrol-2.2\main\logs\frameworkProject。
2.创建CVS的工作目录:C:\BuildServer\workingCopy\frameworkProject。
3.创建工作目录的代码库目录:C:\BuildServer\workingCopy\frameworkProject\lib。
4.创建工作目录的单元测试报告目录:C:\BuildServer\workingCopy\frameworkProject\reports\junit\data。
5.创建工作目录的jar文件存放目录:C:\BuildServer\workingCopy\frameworkProject\dist。

初始化代码工作目录
在C:\BuildServer\workingCopy\frameworkProject目录下,使用WinCVS CheckOut src模块:cvs co src。

准备项目的依赖代码库
把构建项目的依赖代码库复制到C:\BuildServer\workingCopy\frameworkProject\lib。完成之后的目录结构如上图所示。

编写项目构建脚本
构建脚本build.xml和build-cc.xml位于C:\BuildServer\workingCopy\frameworkProject目录下。其中build.xml是项目的Ant构建脚本,而build-cc.xml负责帮助CruiseControl从CVS服务器上update源代码到本地的工作目录,然后调用build.xml构建项目。
build-cc.xml
<?xml version="1.0"?>
<project name="CruiseControlWrapperForFrameworkProject" default="build" basedir="."> <target name="update">  <!-- update the working copy from the cvs -->  <exec executable="cvs">   <arg line="-d :pserver:talent:talent @10.75.140.128:/cvsroot update src"/>  </exec> </target>  <target name="build" depends="update">  <!-- invoke the project's build script -->  <ant antfile="build.xml" target="jar"/> </target></project>
build.xml
<?xml version="1.0"?><project name="frameworkProject" default="compile">    <description>        This is our framework project which we're putting on CruiseControl    </description>        <target name="setup" depends="setup.properties, setup.paths" />    <target name="setup.properties">        <property name="src.main" value="src" />        <!--<property name="src.test" value="src/test" />-->        <property name="classes" value="classes"/>        <property name="classes.main" value="${classes}/main" />        <!--<property name="classes.test" value="${classes}/test" />-->        <property name="libs" value="lib" />        <property name="dist" value="dist" />        <property name="reports" value="reports" />        <property name="reports.junit.data" value="${reports}/junit/data" />    </target>    <target name="setup.paths">        <path id="classpath.main">            <pathelement location="${classes.main}" />        </path>        <path id="classpath.lib">         <fileset dir="${libs}">          <include name="**/*.jar" />         </fileset>        </path>    </target>    <target name="clean" depends="setup">        <delete dir="${classes}" failonerror="false" />        <delete dir="${reports}" failonerror="false" />        <delete dir="${dist}" failonerror="false" />    </target>    <target name="compile" depends="setup, compile.main" />    <target name="compile.main" depends="setup">        <mkdir dir="${classes.main}" />        <javac srcdir="${src.main}" destdir="${classes.main}">         <classpath refid="classpath.lib" />        </javac>    </target>    <target name="compile.tests" depends="setup">        <mkdir dir="${classes.test}" />        <javac srcdir="${src.test}"               destdir="${classes.test}"               classpathref="classpath.lib" />    </target>    <target name="test" depends="compile">        <delete dir="${reports.junit.data}" failonerror="false" />        <mkdir dir="${reports.junit.data}" />        <junit printsummary="yes" haltonfailure="no"                failureproperty="tests.failed">            <classpath refid="classpath.lib" />            <formatter type="xml" />            <batchtest fork="yes" todir="${reports.junit.data}"                        failureproperty="tests.failed">                <fileset dir="${src.test}">                    <include name="**/*Test*.java" />                    <exclude name="**/AllTests.java" />                </fileset>            </batchtest>        </junit>        <fail if="tests.failed" message="Some unit tests failed" />    </target>    <target name="jar" depends="compile, test">        <mkdir dir="${dist}" />        <jar destfile="${dist}/framework.jar" basedir="${classes.main}" />    </target>    <target name="all" depends="jar" /></project>

配置CruiseControl
config.xml
下面以一个简单的config.xml文件为例,说明CruiseControl的配置方法。配置文件config.xml位于C:\BuildServer\cruisecontrol-2.2\main\bin目录下,和启动脚本cruisecontrol.bat放在一起。
<?xml version="1.0"?><cruisecontrol> <project name="frameworkProject">  <dateformat format="yyyy/MM/dd HH:mm:ss" />  <bootstrappers>   <currentbuildstatusbootstrapper file="../logs/currentbuild.txt" />  </bootstrappers>  <modificationset quietperiod="60" >   <cvs LocalWorkingCopy="http://www.cnblogs.com/../workingCopy/frameworkProject/src"/>  </modificationset>  <schedule interval="3600" >   <ant antWorkingDir="http://www.cnblogs.com/../workingCopy/frameworkProject" buildfile="build-cc.xml" />  </schedule>  <log dir="../logs/frameworkProject">   <merge dir="http://www.cnblogs.com/../workingCopy/frameworkProject/reports/junit/data"/>  </log>  <publishers>   <currentbuildstatuspublisher file="../logs/currentbuild.txt" />   <artifactspublisher dir="http://www.cnblogs.com/../workingCopy/frameworkProject/dist" dest="../logs/frameworkProject" />   <htmlemail mailhost="talenttech.com.cn"    returnaddress="buildmaster@talenttech.com.cn"     subjectprefix="[Talent Build Server]"//自己替换[]buildresultsurl="http://10.75.140.128:9000/cruisecontrol/buildresults/ ;frameworkProject"logdir="C:\BuildServer\cruisecontrol-2.2\main\logs\frameworkProject"            xsldir="C:\BuildServer\cruisecontrol-2.2\reporting\jsp\xsl"css="C:\BuildServer\cruisecontrol-2.2\reporting\jsp\css\cruisecontrol.css">    <failure address="liutao@talenttech.com.cn" />    <success address="liutao@talenttech.com.cn" />    <success address="wangkai@talenttech.com.cn" />    <success address="wangchuang@talenttech.com.cn" />    <success address="wu.gaofeng@126.com" />   </htmlemail>  </publishers> </project></cruisecontrol>

<cruisecontrol>
<cruisecontrol>是配置文件的根元素,它可以拥有一个或多个<project>子元素。本例中它拥有一个项目名为frameworkProject。

<project>
<project>元素是一个完整的build任务,包括检查配置管理库是否有新的修改,构建项目并发布项目构建结果。它告诉CruiseControl构建什么,何时构建,如何构建以及如何发布构建报告。它有一个必需的属性name和一个可选属性buildafterfailed。
属性buildafterfailed定义了当构建失败时,是否要继续进行,缺省是"true"。
<project>元素的子元素包括,<bootstrappers>,<modificationset>,<schedule>,<log>,<publishers>,<dateformat>和<plugin>,其中<modificationset>和<schedule>是必需的元素。

<dateformat>
<dateformat>用于定义日期的格式,缺省格式是:MM/dd/yyyy HH:mm:ss。

<bootstrappers>
<bootstrappers>元素是启动任务Plugin的容器,用于定义构建任务启动前需要执行的任务。常用的Plugin包括:
1. <currentbuildstatusbootstrapper>,定义一个CruiseControl的构建状态信息文件。CruiseControl的Build Result JSP从该文件读取状态信息并显示在页面上。属性file用于指定构建状态文件目录和文件名。
2. <cvsbootstrapper>,用于在项目构建开始前从CVS服务器上update指定的文件。通常可以用于更新项目的构建脚本。属性localWorkingCopy指定CVS本地工作目录,属性file指定需要update的文件名,相对于属性localWorkingCopy指定的目录。

<modificationset>
<modificationset>元素用于告诉CruiseControl是否需要构建项目,即配置管理库的代码是否存在更新。它拥有两个可选属性requiremodification和quietperiod。
属性requiremodification告诉CruiseControl,在配置管理库没有代码更新的情况下,是否需要构建。缺省为"true",即没有更新则无须进行构建。
属性quietperiod告诉CruiseControl,最新一次代码提交后CruiseControl需要等待的时间(秒)。用于防止CruiseControl在开发人员提交代码时进行项目构建。缺省为"60"秒。
在本例中使用<cvs>来检查和工作目录相关的代码在CVS配置管理库是否有更新。<cvs>使用"cvs log"命令来检查最新更新工作目录和当前代码库的差异。

<schedule>
到目前为止,以上的配置文件内容已经定义了CruiseControl构建什么以及何时构建。<schedule>元素告诉CruiseControl每隔多长时间(秒)启动一次构建任务。它有一个可选的属性interval,用于定义以秒为单位的时间间隔。缺省为"300"秒。
在本例中,属性interval设为"3600",这意味着CruiseControl每隔一个小时使用<modificationset>定义的任务检查一次代码库。
<schedule>元素拥有三个子元素<ant>,<maven>和<parse>。
<ant>子元素告诉CruiseControl何时或每隔几次运行Ant来构建项目。
在本例中,antWorkingDir属性设定Ant的工作目录,buildfile属性设定构建脚本build file的目录。
属性multiple告诉CruiseControl每隔几次执行一次本<ant>任务。
除此之外,还可以指定Ant的运行时间(time属性),build file的target(target属性,不设定则为build file的缺省target)。
请参见CruiseControl的配置文档(位于${CruiseControl_Home}/main/docs目录下)。

<log>
<log>元素设定CruiseControl日志文件的存放目录,并通过<merge>子元素指定合并什么样的XML文件(构建过程中产生的文件)到CruiseControl的日志文件中。
<merge>子元素的pattern属性定义匹配的文件名模式,缺省为".xml";dir属性用于指定一个目录,这个目录下所有匹配模式的文件将合并到CruiseControl的日志文件中。

<publishers>
<publishers>元素用于指定构建任务结束后,CruiseControl如何发布项目构建结果。项目构建结果的发布方式可以是Email,网页,复制代码库到指定的目录,或是发布代码库到FTP服务器。
在本例中,共有<currentbuildstatuspublisher>,<artifactspublisher>和<htmlemail>三个publisher。
<currentbuildstatuspublisher> publisher把下次构建的时间写入指定文件,文件名由file属性设定。
<artifactspublisher> publisher元素把项目构建产品复制到指定的目录,dir属性定义源目录,dest定义目标目录的父目录(实际目录还要加上构建时的时间戳,如:父目录/19890604203828)。
<htmlemail> publisher把构建结果以HTML格式通过Email发布。缺省情况下,HTML格式的Emai和CruiseControl Web应用的构建结果JSP页面相同。
本例中<htmlemail>的属性和子元素的作用很容易理解,更多的配置项参见联机文档。

创建CruiseControl的Web应用
1. 在C:\BuildServer\cruisecontrol-2.2\reporting\jsp目录下运行build war命令;
2. 提示设置user.log.dir属性时,输入C:\BuildServer\cruisecontrol-2.2\main\logs;
3. 提示设置user.build.status.file属性时,输入currentbuild.txt;
4. 提示设置cruise.build.artifacts.dir属性时,输入/artifacts/frameworkProject;
5. Web应用构建完成后,可以在C:\BuildServer\cruisecontrol-2.2\reporting\jsp\dis目录下找到cruisecontrol.war文件;
6. 把cruisecontrol.war复制到tomcat的webapps目录下,发布CruiseControl的Web应用;

启动CruiseControl
使用C:\BuildServer\cruisecontrol-2.2\main\bin目录下cruisecontrol.bat启动CruiseControl。用法如下:
C:\BuildServer\cruisecontrol-2.2\main\bin>cruisecontrol [options]
命令cruisecontrol的选项包括:
-port [number] JMX服务器的Http Controller的端口;缺省为8000
-rmiport [number] JMX服务器的RMI Controller的端口;缺省为1099
-xslpath directory JMX的XSL文件存放目录;
-configfile file 配置文件的路径;缺省为当前目录下的config.xml文件
-debug 将CruiseControl内部的日志级别调整到DEBUG
只有指定port和/或rmiport属性时,JMX服务器才启动。如果要修改port参数(不使用缺省的8000端口),必须要修改reporting/jsp目录下的controlpanel.jsp文件,然后再重新创建和发布CruiseControl的Web应用;也可以直接修改Tomcat\webapps\cruisecontrol目录下的controlpanel.jsp页面。

CruiseControl的Web界面
在浏览器地址栏输入:http://BuildServer-IP:9000/cruisecontrol/,可以访问CruiseControl的Web应用,如下图所示:




在左侧区域,按时间顺序列出最新Build结果的连接。如果Build结果超过10个,左侧区域只显示10个连接,其余的Build结果可以在下面的下拉框中找到。上图中因为Build结果少于10个,所以下拉框是空的。
当点击右上角的Control Panel按钮时,如果出现错误,则是因为没有使用CruiseControl的JMX支持。要启动JMX支持,请看下节内容。

使用JMX控制台
要使用CruiseControl的JMX控制台很简单,只需在启动cruisecontrol时指定JMX Server的Http Controller端口即可:cruisecontrol -port 8000。
进入CruiseControl的Web应用界面,点击右上角的Control Panel按钮,则出现CruiseControl的"JMX Control Panel"。
可以通过JMX控制面板在运行时修改配置,而不需要重启CruiseControl。点击CruiseControl Project MBean,尝试它的管理功能。
例如,可以修改MBean的"BuildInterval"属性,来更改项目构建的时间间隔;修改ConfigFileName,则可以更改CruiseControl的配置文件;点击MBean的build操作"invoke"按钮,则可以强制启动项目构建任务。

持续集成资源
1. 持续集成:http://www.martinfowler.com/articles/continuousIntegration.html
2. AntHill:http://www.urbancode.com/projects/anthill/
3. CruiseControl:http://cruisecontrol.sourceforge.net/
4. CVSNT:http://www.cvsnt.org/
5. WinCVS中文版:http://www.8848software.com/wincvs/
6. Driving On CruiseControl Part1:
http://www.javaranch.com/journal/200409/DrivingOnCruiseControl_Part1.html
7. Driving On CruiseControl Part2:
http://www.javaranch.com/journal/200410/DrivingOnCruiseControl_Part2.html
8. Scheduled Builds:http://www.pragmaticprogrammer.com/starter_kit/au/scheduled.pdf

Java代码规范检查工具很多,CheckStyle是其中最有名的,它的Eclipse插件,使用非常方便。我在最近的项目中,之所以选择JCSC,是因为JCSC不但可以检查代码规范,而且给出了NCSS(Non Commenting Source Statements)和CCN(Cyclomatic Complexity Number),前者近似等于Java的有效代码行,后者则用于评价类方法的复杂度。

下面的安装配置延续我的Blog,CruiseControl:持续集成工具的例子。

安装JCSC
1、从SourceForge下载JCSC.zip
2、将JCSC.zip解压到D:\BuildServer目录
3、添加JCSC_HOME系统环境变量,本例中为D:\BuildServer\jcsc
4、添加JCSC_HOME/bin到系统路径

配置ANT
1、把JCSC_HOME/jcsc/lib的jar文件复制到/ANT_HOME/lib

配置CruiseControl
1、把JCSC_HOME/lib的jar文件复制到DEFAULT_CCDIR/main/lib
2、修改cruisecontrol.bat,在cruisecontrol.jar之前添加%LIBDIR%\JCSC.jar,在类路径后面添加%LIBDIR%\xercesImpl.jar
3、复制/JCSC_HOME/html/xml/xsl/cruisecontrol/jcsc.xsl文件到TOMCAT_HOME//webapps/cruisecontrol/xsl目录
4、修改TOMCAT_HOME//webapps/cruisecontrol/xsl/jcsc.xsl文件,把JCSC Details的链接改成"/cruisecontrol/jcsc/index.html"
5、在TOMCAT_HOME/webapps/cruisecontrol/buildresults.jsp页面中添加


6、在webapps/cruisecontrol目录下创建jcsc目录
7、复制JCSC_HOME/html/xml目录下的xsl子目录和index.html文件到webapps/cruisecontrol/jcsc目录
8、复制JCSC_HOME/html/web/jcsc-result/rules.jcsc.xml文件到webapps/cruisecontrol/jcsc目录

9、把JCSC生成的overview.xml合并到CruiseControl的log.xml文件。修改CruiseControl的config.xml文件,如
<log dir="../logs/frameworkProject">
<merge dir="http://www.cnblogs.com/../workingCopy/frameworkProject/reports/junit/data" />
<merge file="D:\BuildServer\tomcat-4.1.31\webapps\cruisecontrol\jcsc\overview.xml" />
</log>

修改ANT的构建脚本build.xml
1、添加JCSC属性
<!-- jcsc home config -->    <property name="jcsc.home" value="D:\BuildServer\jcsc" />    <property name="jcsc.log.dir" value="D:\BuildServer\tomcat-4.1.31\webapps\cruisecontrol\jcsc" />    <property name="jcsc.rules.dir" value="D:\BuildServer\tomcat-4.1.31\webapps\cruisecontrol\jcsc\rules.jcsc.xml" />
2、定义JCSC的ANT Task
<taskdef name="jcsc" classname="rj.tools.jcsc.ant.JCSCTask" />
3、<target name="clean">中添加删除JCSC日志文件的任务
<delete failonerror="false">        <fileset dir="${jcsc.log.dir}" includes="*.xml" excludes="xsl,rules.jcsc.xml,index.html" />
4、添加JCSC Target
<target name="jcsc">  <jcsc rules="${jcsc.rules.dir}" destdir="${jcsc.log.dir}">   <fileset dir="${src.main}" includes="**/*.java" />  </jcsc> </target>

5、调用JCSC Task
<target name="all" depends="clean, jar, test, coverage.report, jcsc" />
使用Rules Editor修改Rules文件
1、运行JCSC_HOME\bin\ruleseditor.bat命令,使用Rules Editor编辑Rules定义文件,可以编辑Open Brace'{' On New Line,Class/I-Face Header等选项
2、也可以直接修改rules.jcsc.xml文件

集成在CruiseControl的JCSC报告如下图所示。



在htmlmail邮件中集成JCSC报告
要在CruiseControl的htmlmail邮件中集成JCSC报告,需要修改CruiseControl的代码。在HTMLEmailPublisher类的String[] xslFileNames变量中加入jcsc.xsl,即可在htmlmail中包含JCSC报告的概要部分。

posted @ 2008-02-24 00:29  kingkoo  阅读(3676)  评论(0编辑  收藏  举报