`
kidiaoer
  • 浏览: 806084 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
文章分类
社区版块
存档分类
最新评论

持续集成总结

阅读更多


一、什么是持续集成(Continuous Integration)?

这个名词已经在软件开发领域持续了N年,一个比较简单的定义如下:
持续集成(CI)是一种实践,可以让团队在持续的基础 上收到反馈并进行改进,不必等到开发周期后期才寻找和修复缺陷。通俗一点儿说,就是指对于开发人员的每一次代码提交,都自动地把Repository中所有代码Check out到一个空目录,并且自动运行所有Test Case。如果成功则接受这次提交,否则告诉所有人,这是一个失败的Revision。更具体的解释可以参考Martin fowler的Continuous Integration  。

二、持续集成的价值与成本

有句时髦的话,叫做“存在即为合理”。既然持续集成已经存在了这么长的时间,而且没有消失的迹象,那就是有价值的东西。那么它的价值何在?有人概括如下:(1) 减小风险;(2) 减少手动过程;(3) 生成构建结果;(4) 安全感。
而持续集成的成本在于对持续集成代码的维护成本和集成的时间成本。因为随着项目进行,软硬件环境会越来越复杂,成品代码也会不断膨胀。此时,需要团队而修改或增加原有的测试代码,以适应这些变化,同时,每次集成所需时间也会变长,这就是持续集成的成本。某个blog中提道:“这种集成是如此的频繁,多少次的代码Commit就有多少次持续集成。前提是集成的成本很低,或者说是完全自动化的。”

三、持续集成应该自动化什么呢?

我们要以尽可能少的成本来获得尽可能多的价值。这就要考虑哪些自动化是必要的啦。Jez Humble提到至少有六点要做到自动化,它们分别是(1)自动化的运行测试;(2) 自动产生可部署的二进制成品;(3) 自动将成品自动部署到近似生产环境;(4) 自动为CodeBase打上标签;(5) 自动运行回归测试;(6)自动生成度量报告。

四、持续集成服务器的选择

在 进行持续集成实践前,应当正确的选择并配置持续集成服务器。比较成熟的持续集成服务器包括:CruiseControl, Anthill, Bamboo, TeamCity, Continuum 等。CruiseControl作为开源产品,以其对于各种SCM以及构建工具的广泛支持而被许多开发团队所接受。而开发自动化专家 Duvall 采用一致的评估标准和很多说明性示例,介绍了一些开源 CI 服务器,包括 Continuum、CruiseControl 和 Luntbuild。并指出“要根据 自己的 具体技术和政策需求对工具进行分析”。并用以下五个指标来评估CI工具,它们分别是:(1)  特性;(2)  可靠性;(3)  寿命;(4) 目标环境;(5) 易用性。结果如下表:
CI

而CruiseControl是我唯一真正用过的持续集成工具,它现在灵活而又强大功能也让我瞠目,而且配置与管理也较两年前容易得多啦。为什么说它强大呢?因为你只要想得到的问题,它也都会有所考虑。朋友的Blog上有些CruiseControl的最佳实践足以证明这一点,只要你肯去实践。

五、只有持续集成服务器是远远不够的

正如Jez Humble所说,CruiseControl和其它的CI工具本质上只不过是一个定时器,时间一到,做你让它做的事情。 所以,必然要有其它工具与其结合,方显持续集成的本色。这些工具又是什么呢?想测试的话,你就要用一些测试工具,如 JUnit,JWebUnit,Selenium等等;想检查代码标准的话,你就要用checkstyle等代码规范检查工具;想要了解测试覆盖率的话, 你可能就要用到JCoverage啦。当然,想得到二进制文件,就要用到Ant,Make之类的工具啦。

六、最重要的事:实践与反思

也 许这些东西大家都知道,而且有些人可能已经实践过啦。无论这些实践的结果是怎样的,一定不要忘记总结和反思。如果这些实践成功了,不要把它归功于这个工 具,而是要总结一下为什么会成功,如果你愿意的话,还可以和大家分享一下。如果这些实践失败了,也不要把它归功于这个工具,而是要反思一下,是否正确地使 用了这个工具,团队成员是否都喜欢这个工具,为什么?


CruiseControl是CI服务器的老者,诞生已是多年,在许多方面,CruiseControl服务器已经成为持续集成实践的同义词。而现 在,CruiseControl已发展成为一个家族式系统,包括CruiseControl.java、CruiseControl.net、 CruiseControl.ruby等适应不同语言环境的实现,其强大的插件和扩展能力也是诸多同类系统无法比你的。而在这里,我只介绍该家族的本家 CruiseControl.java,即CruiseControl。CruiseControl是一个不错的持续集成服务器,不过国内社区的普遍反应 是:它的配置太麻烦,无从下手。从本篇文章开始,我将逐步介绍CruiseControl的环境搭建与配置,以及持续集成中的一些实践。我们将从最简单的 使用方式(stand-alone)开始,以便您可以快速开始您的持续集成之旅,而不必面对“千奇百怪”的问题。
好!先让我们简单搭建个持续集成服务器再说吧

一、我使用的CruiseControl版本

   1. CruiseControl是一个java开源项目,我将以它最近的一个分支版本来介绍它的使用。CruiseControl最近发布的版本是V2.8.1。这个分支版本就是在V2.8.1基础之上。
   2. 这个分支版本包含一个Dashboard,可以方便地看到各项目的构建状态。
   3. 这个分支版本当然还支持多个Build Loop,你可以在多个机器上部署独立的Build Loop,从而建立你自己的Build Grid。

二、得到CruiseControl的分支版本

    要得到这个分支版本,你可以使用subversion不用任何密码从CruiseControl的Repository中检出。命令如下:
svn checkout https://cruisecontrol.svn.sourceforge.net/svnroot/cruisecontrol/branches/cce/cruisecontrol/

三、构建CruiseControl

       0.    你机器上至少要安装有JAVA 1.4,并在环境变量中设置有JAVA_HOME,并把java/bin加入到path中。CruiseControl自带了ANT,所以不用事先安装。

   1. 从源代码中构建CruiseControl

       如果从源码中构建CruiseControl,要确保你的机器可以访问互联网。因为构建过程中要检查一些license。另外,最好有SVN 客户端软件,因为在构建时,它会用到javahl。
       在刚刚checkout出来的CruiseControl目录下,有一个批处理文件,名为release.bat。运行这个批处理文件,你会在 target目录下发现一个压缩文件,名这cruisecontrol-bin-2.7.2-dev.zip。将其解压到一个目录,我们这里解压到C盘根 目录下。解压后,目录结构如下:
目录结构
      2.  直接使用已构建完成的CruiseControl
如果你不想做这一步,也可以在binaryrelease目录下发现解压后的文件,把它们拷贝到相应的目录下也可以。

四、运行CruiseControl

   CruiseControl可以运行在多种应用服务器中,当然,它自己也捆绑了一个开源应用服务器,那就是Jetty 6。为了尽快建立我们的环境,我们这里暂时使用它自己捆绑的Jetty,快速开始我们的持续集成之旅。
   直接运行CruiseControl目录下的CruiseControl.bat,它会自行启动Jetty6,当从Console中看到它启动完毕后,你 就可以在浏览器中访问http://localhost:8080/dashboard,此时你会看到一个名为connectFour的示例项目在 Dashboard上显示了。

Build Dashboard

(如果你足够快的话,你会发现Dashboard 上的小方块是从灰色变成深绿色的。为什么呢?我们以后再说。)
“绿色”表示该项目构建通过。如果要是有多个项目的话,应该看到多个小方块。

点击“Builds” Tab页,就到了 Builds 页。这一页会以列表的方式显示所有的项目。当前只有一个项目,所以在列表中只有一行,如下图所示:
Builds Page
在该页面上点击某个项目时,会打开Build Detail页, 显示该项目最近一次Build的信息。页面右侧是该项目所有历史构建信息。你会发现,这时connectFour项目有两个构建历史版本,一个是刚刚构建 完成的,另一个是两年前的构建。这些信息是来自于CruiseControl对该项目的日志文件。此时,点击这两个构建结果中的任何一个,都会打开该次构 建的Build Detail页面,显示相关的构建历史信息。如下图所示:
Build Detail

我们将在下一篇文章中简要介绍CruiseControl的配置文件,为您加入自己的构建项目做准备。



上一篇文章中,我们已经看到CruiseControl开始工作了。接下来我们先了解一个它的配置文件,然后再加入你的项目。
CruiseControl的配置文件的确了比较复杂,一方面是因为:它太灵活,太想完成你想达到的任何效果了。另一方面就是:它在文档化方面的确很落后。你只能在用户组中找答案。(不过,这也是学习的一个途径。如果你了解开源的话,你就会习惯于在用户组中找答案啦。)

再了解配置文件之前,让我们先定义一下我们可能会用到的术语。


   1. 工作目录(也就是CruiseControl的工作目录):是指启动CruiseControl的脚本目录。在Stand-alone情况下,就是文件cruisecontrol.bat所在的目录。如果你把它部署到了应用服务器中,那一般就指bin目录了。

       2.
Log 目录

:就是CruiseControl存储所有构建项目日志信息的目录,默认值为工作目录下名为logs目录。

       3.
Projects目录:

就是CruiseControl存储所有构建项目源代码的目录,默认值为工作目录下名为projects目录。


一、配置文件及其正确位置

      配置文件包括两个,一个名为
config.xml

,另一个名为
dashboard-config.xml

。默认情况下,这两个文件和cruisecontrol.bat在同一下目录下,即
工作目录



     如果在配置文件中,任何路径前面如果没有“/”的话,那就是相对于
工作目录

的。

 

   

      另外,CruiseControl是可以改变他们的位置的,但建议在没有深入了解CruiseControl时,不要试图改变它们,因为我们的目标是让CruiseControl在最短的时间里为你创建价值。


二、Config.xml 文件

<cruisecontrol>
    <project name="connectfour">  --这里的name应该和你在projects目录下的项目名完全一致。

        <listeners>          --用于监听项目状态的变化(指building,passed等)
            <currentbuildstatuslistener file="logs/${project.name}/status.txt"/>   --这里的${project.name}
        </listeners>                                                                                                                 是指“connectfour”

        <bootstrappers>        --用于CruiseControl从Repository更新代码
            <svnbootstrapper localWorkingCopy="projects/${project.name}" />
        </bootstrappers>

        <modificationset quietperiod="30">      --用于监听在quietperiod秒内,Repository是否变化
            <svn localWorkingCopy="projects/${project.name}"/>
        </modificationset>

        <schedule interval="300">  --用于每隔interval秒,CruiseControl去检查并计划一次构建
            <ant anthome="apache-ant-1.7.0" buildfile="projects/${project.name}/build.xml"/>
        </schedule>

        <log>  --用于得到并保存log文件。默认情况下,将放在projects/${project.name}的目录下
            <merge dir="projects/${project.name}/target/test-results"/> --用于将Build中生成的结果合并
        </log>                                                                                                            到log文件中

        <publishers>  --用于将构建的结果(如二进制文件)发布到指定的位置
            <onsuccess>  --决定在构建成功的情况下,发布哪些内容
                <artifactspublisher dest="artifacts/${project.name}" file="projects/${project.name}/target/${project.name}.jar"/>
            </onsuccess>
        </publishers>

    </project>
</cruisecontrol>

三、dashboard-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<!--
/********************************************************************************
这里省略了一些声明
********************************************************************************/
-->
<dashboard>
     <buildloop
        logsdir=""        --这里指定logs目录,一定要与config.xml文件中一致,否则你会有麻烦。
                                                如果为空串,则默认为工作目录下的logs目录。
              artifactsdir="" />  --这里指定artifacts目录,一定要与config.xml文件中一致
                                                   如果为空串,则默认为工作目录下的artifacts目录。
    <features allowforcebuild=""/>  --是否可以在Dashboard上强制构建(force build)
    <trackingtool projectname="" baseurl="" keywords=""/>  --这是用于与Mingle的集成
     <subtabs>   --在这里,你可以自己扩展你自己的subtab页,不过要先实现一些接口。
        <subtab class="net.sourceforge.cruisecontrol.dashboard.widgets.ErrorsAndWarningsMessagesWidget" />
    </subtabs>
</dashboard>

四、配置文件中的注意事项

1、如果logs、artifacts和projects目录不存在,在你运行CruiseControl之前,要手工建立它们。

2、确保两个配置文件中的logs/artifacts目录指向同一个目录。因为CruiseControl core会向其中写数据,而Dashboard会从中读数据。


五、小贴士

在Config.xml文件中,有几个元素的概念要先澄清一下(只针对当前示例,严格定义请参见CruiseControl的文档)。

   1. <listeners> 用于监听状态变化,如waiting for build,queued,building。一般不用改变。
   2. <bootstrappers>用于检出代码,CruiseControl有很多种bootstrapper,示例中使用的是SVN。localWorkingCopy属性是指你把代码检出到安装CruiseControl机器的哪个文件目录。
   3. <modificationset>用于监听Repository的变化频率,如果CruiseControl发现代码有变化,而且在quietperiod秒内没有其它用户检入代码(用于保证被构建的版本完整性),CruiseControl才会检出代码。
   4. <schedule >用于指定CruiseControl去检查Repository的时间间隔。并指定使用哪个构建文件。示例中使用的是ant脚本,构建文件是被检出项目的根目录下的build.xml(即在Repository中)。
   5. <log>用于指定CruiseControl把日志放在哪里,把哪些构建结果放在日志中。如果没有指定任何属性,默认为logs目录下的${project.name}目录。
   6. <publishers> 用于在构建结束后,发布哪些消息和文件。CruiseControl有很多插件可以做各种各样的Publishing。例如,在构建成功后,通过http调用指定的页面,也可以给指定的人发邮件。在失败时,播放音乐,通知team。



好,现在我们已经在自己的机器上建立了一个持续集成服务器,并初步了解它的配置文件。在本文中,我们就要把自己的项目加到其中。 做为一个入门示例,我们先要陈述一下假设条件,以便我们快速推进我们的部署过程。

一、 准备工作(请确认一下)

1、你的项目代码放在Google Code上面(Google Code用的是SVN ),你的项目名称是yourprojectname,项目的源文件就放在trunk目录下。那么,其svn update的链接如下:http://yourprojectname.googlecode.com/svn/trunk/。CruiseControl只会更新文件,不会修改源代码,所以匿名检出就可以了。

2、你的项目是一个JAVA项目,用ANT进行构建,构建文件 名为build.xml,放在项目的根目录上,默认的ant task名为all。确保文件中其中所有路径都是相对于项目根目录的,将其Copy到任何目录下,ant all都可以正确运行。(我们的主要目的是建立环境,使其在今后的开发工作中可以发挥作用,而不是要发现我们构建脚本中错误。)

3、你的CruiseControl工作目录是C:/CruiseControl,方便起见,我们以下将用${CC_HOME}代替。

4、你的logs目录是${CC_HOME}/logs。如果你没有独自改动配置的话,它的位置就如前所述。

5、你的projects目录是${CC_HOME}/projects。如果你没有独自改动配置的话,它的位置就如前所述。

6、你的artifacts目录是${CC_HOME}/artifacts。如果你没有独自改动配置的话,它的位置就如前所述。


二、加入项目

1、手工检出文件:

确保将你的项目代码检出到${CC_HOME}/projects/yourprojectname目录下,即在该目录就是你项目的根目录,可以找到名为build.xml文件。

2、修改配置文件(添加你的项目)

将下面的代码段加入到config.xml文件中,位于<CruiseControl>节点下。


    <project name="yourprojectname"> --这里一定要与你的projects目录下的项目名相同

        <listeners>
            <currentbuildstatuslistener file="logs/${project.name}/status.txt"/>
        </listeners>

        <bootstrappers>
            <svnbootstrapper localWorkingCopy="projects/${project.name}" />
        </bootstrappers>

        <modificationset quietperiod="30">
            <svn localWorkingCopy="projects/${project.name}"/>
        </modificationset>

        <schedule interval="300">            <ant anthome="apache-ant-1.7.0" buildfile="projects/${project.name}/build.xml"/>
        </schedule>

        <log>
            <merge dir="projects/${project.name}/target/test-results"/>
             <!-- 在上面一句,一定要确保构建失败后可以找到这个dir,如果没有,删除这个元素好了-->
        </log>

        <publishers>
            <onsuccess>
                <artifactspublisher dest="artifacts/${project.name}" file="projects/${project.name}/target/${project.name}.jar"/>
                <!-- 在上面一句,一定要确保你打包出来的文件名是yourprojectname.jar -->
            </onsuccess>
        </publishers>

    </project>

最终的文件如下:

<cruisecontrol>
    <project name="connectfour">

        <listeners>
            <currentbuildstatuslistener file="logs/${project.name}/status.txt"/>
        </listeners>

        <bootstrappers>
            <svnbootstrapper localWorkingCopy="projects/${project.name}" />
        </bootstrappers>

        <modificationset quietperiod="30">
            <svn localWorkingCopy="projects/${project.name}"/>
        </modificationset>

        <schedule interval="300">
            <ant anthome="apache-ant-1.7.0" buildfile="projects/${project.name}/build.xml"/>
        </schedule>

        <log>
            <merge dir="projects/${project.name}/target/test-results"/>
        </log>

        <publishers>
            <onsuccess>
                <artifactspublisher dest="artifacts/${project.name}" file="projects/${project.name}/target/${project.name}.jar"/>
            </onsuccess>
        </publishers>

    </project>

    <project name="yourprojectname">

        <listeners>
            <currentbuildstatuslistener file="logs/${project.name}/status.txt"/>
        </listeners>

        <bootstrappers>
            <svnbootstrapper localWorkingCopy="projects/${project.name}" />
        </bootstrappers>

        <modificationset quietperiod="30">
            <svn localWorkingCopy="projects/${project.name}"/>
        </modificationset>

        <schedule interval="300">
            <ant anthome="apache-ant-1.7.0" buildfile="projects/${project.name}/build.xml"/>
        </schedule>

        <log>
            <merge dir="projects/${project.name}/target/test-results"/>
        </log>

        <publishers>
            <onsuccess>
                <artifactspublisher dest="artifacts/${project.name}" file="projects/${project.name}/target/${project.name}.jar"/>
            </onsuccess>
        </publishers>

    </project>

</cruisecontrol>

三、看一下结果

(1) CruiseControl会自动更新这个配置文件,加载时间一般是在任何一个项目需要检查是否有更新时。

(2) 你会看到这个项目在Dashboard上首先是灰色的方块,因为这个项目还从来没有在CruiseControl上构建过,没有任何历史信息,我们称该项目为"inactive"。

(3) 过一会儿,你会看到一个环形动态图标,表示它在构建中(执行build.xml脚本)。

(4) 当环形动态图标消失时,你会看到它变成红色(如果构建失败的话)或绿色(如果构建成功的话)。

我新加的项目名是vcdstore,现在我的dashboard上有两个项目了。



小贴士:

(1) 目前CruiseControl还不支持首次自动检出代码,所以你要先把项目代码手工检出到本地。

(2) 如果你的SVN使用https方式更新代码,你一定要在命令行方式检出代码,并选择永久接受证书。

(3) CruiseControl这个分支版本不支持在UI上增加项目。

(4) 如果你在config.xml文件中把你的项目删除,却不删除它的日志的话,你在Dashboard上还是可以看到这个项目,此时它被标记为"Discontinued",表示你可以看到它的历史信息,但CruiseControl不会再构建它了。

下一篇我们将讲述CruiseControl的一些界面信息元素。



现在,我们已经为自己的项目建立了持续集成服务器。让我们来看一下CruiseControl正在对你说些什么。

一、项目的状态

首先CruiseControl把项目分成三类,Discontinued,Inactive,及Active。

如果一个项目是Discontinued,表示CruiseControl可以找到该项目的日志文件,但在配置文件 (Config.xml)中并没 有该文件。所以CruiseControl不会去构建它,但你可以看到这个项目过去构建的历史信息。如果想把这个项目从CruiseControl中删 除,只有把该项目的日志目录删除才行。如果该项目名为"vcdstore",目录当该是${CruiseContor.Home}/logs /vcdstore。

如果一个项目是Inactive,表示CruiseControl在配置文件(Config.xml)中发现了该项目,但是没有发现关于这个项目的 任何历史信息,即在CruiseControl的日志目录中还没有该项目的日志文件,或日志文件被人为删除了。CruiseControl会根据配置信息 对这个项目进行检查新版本并进行构建。当第一次构建完成后,CruiseControl就会生成日志,这个项目状态就会转为Active了。

如果一个项目是Active,表示CruiseControl即可以找到该项目的日志文件,又在配置文件(Config.xml)中可以发现它。此时,这个项目可能是构建成功,也可能是构建失败,还可能是构建中。

二、Dashboard

你可以通过 http://localhost:8080/dashboard 访问Dashboard。

Dashboard主要有四个页面,它们分别是Dashboard,Builds,Build Details和Administatiorn。

(1) Dashboard

你可以在Dashboard上看到所有项目的状态,
红色方块

表示该项目最近一次构建是失败的。
绿色方块表示该项目最近一次构建是成功的。灰色方块表示该项目可能是Inactive的,也可能是Discontinued。

当把鼠标放在小方块上时,会显示该项目的主要信息。点击小方块,会进入Build Details页面。

Dashboard page

(2) Builds

你可以在Builds页面上以列表的方式看到所有项目的状态,点击每个列表,可以进行Build Details页面。

如果你将ForceBuild配置为true,在列表右侧有一个按钮,你可以强迫该项目进行构建,而不必等到其下一次检查,也不必等到它有版本变化。


(3) Build Details

此页面会列出该项目某次构建的详细信息,包括与上次构建相比有哪些变化,测试结果是什么,详细的日志输出,如果构建成功的话,在配置文件(config.xml)中配置的Artifacts也会在名为Artifiacts的tab页上看到。

右侧的列表是该项目所有的构建列表,点击其中一个构建,你就可以得到该次构建的详细信息。


(4) Administration

该页面你可以看到About和Configuration两个子标签。

在About子标签中,你可以看到CruiseControl所用的环境信息,如CruiseControl的版本号、所用的操作系统和JDK版本等。


在Administration子标签中,你可以看到CruiseControl的Dashboard-config.xml文件内容。在该版本中,还不支持修改,也移除了"Add Project"按钮。


三、CruiseControl Reporting

你也可以通过链接http://localhost:8080/ 来访问CruiseControl的Old Reporting。


点击项目名称,可以看到详细内容。


四、CruiseControl JMX控制台

你也可以通过链接http://localhost:8000/来访问CruiseControl的JMX控制台。


在控制台上点击项目名称(如connectfour),可以修改项目配置,暂停/恢复项目构建等。

小贴士:

(1) 以上的链接均以不修改CruiseControl默认配置为基础。

(2) 后面的文章中,我们会介绍如何通过Build Grid来推展我们的CruiseControl构建能力。


如果您一直跟着这个Thread,那么现在你应该已经可以使用CruiseControl来进行持续集成了。如果你有很多项目需要持续集成的话,可 能在同一时刻会有很多项目排队等待构建的现象,以至于使各项目团队无法得到及时的反馈,此时一个集成服务器就不够了。下面我们就来扩展我们的Build Grid吧。

一、前提条件与假设

根据前面的介绍,你的第一台持续集成服务器已经可以正常运行了。这里列出如下假设,以方便后续描述。

(1) 首台持续集成服务器IP地址为:192.168.1.6,hostname为CI_One。

(2) 在首台持续集成服务器上:

CruiseControl的根目录是:C:/CruiseControl,今后用${CC.HOME}表示。

CruiseControl的Projects目录是:C:/CruiseControl/Proects,今后用${CC.Projects}表示。

CruiseControl的Logs目录是:C:/CruiseControl/Logs,今后用${CC.Logs}表示。

CruiseControl的Artifacts目录是:C:/CruiseControl/Artifacts,今后用${CC.Artifacts}表示。


(3) 你将在IP地址为:192.168.1.8,hostname为CI_Two的机器上部署第二个持续集成服务器。

(4) 把名为CI_One的机器做为主服务器,即把CI_Two上构建的日志(Logs)和结果(Artifacts)放发布到CI_One的${CC.Logs}和${CC.Artifacts}中。

(5) 你将在CI_Two上构建名为CI_TWO_connectFour的项目,它也使用SVN作为项目的SCM。

二、准备工作

(1) 将CI_One上的${CC.Logs}和${CC.Artifacts}设为共享目录,保证CI_Two对这两个目录可写。

(2)可以在CI_Two上,把CI_One的${CC.Logs}和${CC.Artifacts}两个目录分别映射成两个网络驱动器,名字为别为Z:/和Y:/。

(3) 将CI_One上的CruiseControl,整个拷贝到CI_Two上的C盘根目录下,即对于CI_Two来说,CruiseControl的工作目 录是C:/CruiseControl。(当然,也可以把一个你编译后生成的CruiseControl拷贝过来。)

(4) 在CI_Two的${CC.Projects}目录下,建立名为CI_TWO_connectFour的目录,把SVN中的源代码checkout到这个目录下,并确保build.xml文件在这个目录下。

三、第二台持续集成服务器的配置工作

以下所有工作全部发生在名为CI_Two这台机器上。

(4) 修改配置文件config.xml,如下所示:


<cruisecontrol>
    <project name="CI_TWO_connectFour">

        <listeners>
            <currentbuildstatuslistener file="Z:/${project.name}/status.txt"/>
                  <!-- 请注意这里的z: -->
        </listeners>

        <bootstrappers>
            <svnbootstrapper localWorkingCopy="projects/${project.name}" />
        </bootstrappers>

        <modificationset quietperiod="30">
            <svn localWorkingCopy="projects/${project.name}"/>
        </modificationset>

        <schedule interval="300">
            <ant anthome="apache-ant-1.7.0" buildfile="projects/${project.name}/build.xml"/>
        </schedule>

        <log dir="Z:/">
                <!-- 请注意这里的z: -->
            <merge dir="projects/${project.name}/target/test-results"/>
        </log>

        <publishers>
            <onsuccess>
                <artifactspublisher dest="Y:/${project.name}" file="projects/${project.name}/target/connectfour.jar"/>
            <!-- 请注意这里的Y: -->
                </onsuccess>
        </publishers>

    </project>
</cruisecontrol>

(5) 修改启动脚本

打开CruiseControl.bat文件,找到下面这行代码:


set EXEC=%JAVA_PATH% %CC_OPTS% -Djavax.management.builder.initial=mx4j.server.MX4JMBeanServerBuilder -jar "%LAUNCHER%" %* -jmxport 8000 -webport 8080 -rmiport 1099

将下面这段代码:


-dashboardrul http://192.168.1.6:8080/dashboard

加入到上面的代码后,最后的代码如下:


set EXEC=%JAVA_PATH% %CC_OPTS% -Djavax.management.builder.initial=mx4j.server.MX4JMBeanServerBuilder -jar "%LAUNCHER%" %* -jmxport 8000 -webport 8080 -rmiport 1099 -dashboardrul http://192.168.1.6:8080/dashboard

保存该文件。

四、运行Build Grid

(1) 运行${CC.Home}中的CruiseControl.bat,启动CI_One上的CruiseControl。

(2) 运行${CC.Home}中的CruiseControl.bat,启动CI_Two上的CruiseControl。

五、访问你的Build Grid

(1) 在任何一台可以访问CI_One的机器上,通过浏览器访问下面的网址http://192.168.1.6:8080/dashboard 来查看你构建的项目。当然,使用hostname也可以。因为CruiseControl内置了一个Jetty应用服务器,版本是6.1。

(2) 你同样可以使用http://CI_One:8080/ 访问CI_One的Old Reporting 页面,用http://CI_Two:8080/访问CI_Two的Old Reporting。不过你会发现,你在CI_Two上配置的项目状态在CI_One的Old Reporting页面都显示为?????。这就是Old Reporting 的局限性。

小贴士:

(1) 要保证所有项目(无论在哪台机器上构建)的项目名都不相同,否则,CruiseControl的DashBoard只会选取其中的一个同名项目,忽略其它的同名项目。

(2) 在CI_Two中启动脚本中,一定要正确填写DashBoardURL参数,确保是CI_One的地址和端口。

(3) 如果项目的log文件比较大,或者构建时使用内存较多,在启动脚本中,可以通过增加JAVA命令行参数,扩大JVM的内存,以免出现OutOfMemory错误。


到此为止,CruiseControl的入门篇就结束了。以后,作为CruiseControl的进阶篇,我还将继续介绍CruiseControl的一些详细配置。



CruiseControl倾向于通过源码变化来自动触发构建,但仍提供了几种方式来定时触发构建,以达到nightly building的效果。

在开始配置之前,先说明一下config.xml文件中Project元素的相关属性。

    * Project 元素对应你的一个需要构建的项目。其中:

          o name为其指定名称。

          o buildafterfailed 表示本次构建失败,是否要求CruiseControl继续下一次构建。
                + 当设置为true时,表示如果本次构建失败,即使没有任何人检入代码,间隔时间一到,CruiseControl也构建它。
                + 当设置为false时,表求如果本次构建失败,如果没有任何人检入代码,CruiseControl就不会再次构建它。
          o requireModification表示是否需要源文件发生变化才进行构建。
                + 当设置为true时,CruiseControl会根据modificationset元素的设置对源文件进行检查。
                + 当设置为false时,CruiseControl仅根据Schedule元素的设置进行定时构建。
                + 其默认值是ture。
          o forceOnly表示是否只能手工构建。
                + 当设置为true时,用户只能通过手工启动该项目的构建。
                + 当设置为false时,表示即可以根据条件自动构建,用户也可以通过手工构建(前提dashboard-config.xml中的配置必须是true)。
                + 其默认值是true。


为了达到定时构建的目的,首先要将Config.xml文件中该项目Project元素的requireModification属性设置为false。

一、每小时触发一次

设置Schedule元素的interval属性值为3600,即表示每小时会计划构建一次,由于requireModification已经设置为false,所以无论如何,CruiseControl都会构建。例如:

        <schedule interval="3600">
            <ant anthome="apache-ant-1.7.0" buildfile="projects/${project.name}/build.xml" />
        </schedule>


二、每天触发一次

       主要使用Schedule元素中各种构建器(如ant,nant,rake等)的time属性。time属性的格式为hhmm,例如2300就表示晚上十一点。

        <schedule>
            <ant anthome="xxxxxx" buildfile="projects/${project.name}/xxxxx.xml" time="2300"/>
        </schedule>  

当然,也可以使用pause子元素来指定在某段时间内不构建,如白天上班时间不构建,其它时间每隔一小时构建一次可用如下配置表示:

        <schedule interval="3600">
            <ant anthome="apache-ant-1.7.0" buildfile="projects/${project.name}/build.xml"/>
            <pause starttime="0800" endtime="1800"/>
        </schedule>


三、每周触发一次

       主要使用Schedule元素中各种构建器(如ant,nant,rake等)的time及day属性。day属性值为英文星期几且大小写不敏感,如sunday。

        <schedule>
            <ant anthome="apache-ant-1.7.0" buildfile="projects/${project.name}/build.xml"  day="Sunday"/>
        </schedule>

如果想指定具体时间,可以同时使用time属性,例如星期日的晚上十一点可以表示如下:

        <schedule>
            <ant anthome="apache-ant-1.7.0" buildfile="projects/${project.name}/build.xml" time="2300" day="Sunday"/>
        </schedule>

需要说明的是,如果项目的构建时间长于指定的时间间隔,则构建次数会少于理想次数。例如设置每小时构建一次,但是每次构建要花费一个半小时。

另外,day属性还不支持多天,即不支持day="Monday Wednesday Friday"这种设置方式。如果想达到每隔一天构建一次的话,只能使用Schedule的interval属性。


个人建议,CruiseControl最好将触发事件写在各种Builder之外,而当前情况是:如果该Builder不支持time和day属性(如exec),可能就无法象上面所说的那样配置了。






持续集成的七项最佳实践

   1.

      经常提交代码
      (注:就是要常提交、早提交代码,对系统没有太大影响的代码要尽早提交,这样才能实现CI的好处,开发者才能利用最新的变更的代码)
   2.

      不要提交无法构建的代码
      (注:不要将无法构建的代码提交到版本控制库中,以一种可重复的方式编译和测试代码,在向版本控制库提交代码之前先执行私有构建)
   3.

      立即修复无法集成的构建
      (注:就是没有能够报告成功的构建。可能存在编译错误,测试或审查失败,数据库问题或部署失败)
   4.

      编写自动化的开发者测试
      (注:为了在CI系统中运行测试,所有测试都必须自动化)
   5.

      必须通过所有的测试和审查
      (注:自动化的测试和编译同样重要,没有完全通过测试的代码将导致低品质的软件)
   6.

      执行私有构建
      (注:为了防止集成构建失败,开发者应该在完成他们的单元测试之后,在自己本地工作 IDE中模拟一次集成构建)
   7.

      避免签出无法构建的代码
      (注:当集成构建失败时,不要从版本控制库中取出新的代码。否则,你必须花时间找到一些方法来绕过让构建失败的那些错误,才能编译和测试代码)
   8.

      自动化构建
      (注:编写自动化的构建脚本,减少软件项目中手工的、重复的容易出错的过程)
   9.

      执行单命令构建
      (注:把所有需要构建的代码放到源代码版本控制库中,以便于通过单个命令就能构建整个系统)
  10.

      将构建脚本从IDE分离
      (注:有两个原因:1.每个开发者可能使用不同的IDE,而找出每种IDE(是集成开发环境,分为:eclipse(IBM)、NetBean(SUN)、IDEA等)中配置文件的差别会很困难。2.CI必须在无人干预的情况下执行自动化的构建,因此,开发执行的自动化构建脚本也应该能够由CI执行)
  11.

      集中放置软件资产
      (注:使用版本控制库来存放所有文件,可以更好地实现单命令构建。而且,集中放置软件资产防止了“在我的机器上是可以工作的”这种情况,在这种情况下,开发者不能重现其他环境中出现的缺陷)
  12.

      创建一致的目录结构
      (注:为了能够在项目开发过程中从版本控制库中取出所有各种可供使用的资产组合)
  13.

      让构建快速失败
      (注:尽可能把最容易出现错误或失败的放在前面,然后,进行处理)
  14.

      针对所有环境构建
      (注:改进构建的可配置性,将构建脚本参数化,在任何环境下创建能工作的软件)
  15.

      使用专门的构建计算机
      (注:可以大大减少关于环境和配置的假定,当开发者得知最新的集成构建失败时,就可以避免从版本控制库中取出有问题的代码)
  16.

      使用CI服务器
      (注:通过IC服务器来进行持续集成.手工集成比较复杂)
  17.

      执行手工集成构建
      (注:在某个时间只有一个人能向版本控制库提交变更,有效的防止失败的构建,对于大型团队并不太适合)
  18.

      执行快速构建
      (注:如果构建需要很长时间才能完成,常常会给CI的实践投下令人不快的阴影。
      集成构建的时间越短,您就能越快收到反馈信息)
  19.

      分阶段构建
      (注:先是执行初步的集成“提交”或轻量级构建,对软件组建进行集成并执行单元测试,
      在执行更全面的集成构建,包括组件测试、审查和部署)
  20.

      自动化数据库集成
      (注:Ant提供了一个任务,通过sql任务来执行SQL脚本,以一种序列化的方式执行数据库集成)
  21.

      使用本地数据库沙盒
      (注:创建一个数据库的本地实例,在他的工作站上对数据库进行修改,测试变更不会影响到他人,修改、测试通过后并将提交到版本控制库)
  22.

      利用版本控制库共享数据库资产
      (注:将所有数据库资产都放到版本控制库中,利用版本控制库中的脚本从头创建整个数据库,减少项目中所有开发者都要找DBA修改数据库所引起的瓶颈)
  23.

      让开发者能够修改数据库
      (注:让每一个开发者都能够修改任何一部分数据库脚本,并不是每一个开发者都会去修改数据库脚本,因为每个开发者都有自己的数据库沙盒)
  24.

      让DBA成为开发的一员
      (注:让DBA成为开发团体的一员,这样方便开发者修改数据库)
  25.

      自动化单元测试
      (注:使单元测试(即测试方法)实现自动化)
  26.

      自动化组件测试

      (注:验证系统的各个部分,可能需要安装整个系统或某些外部依赖关系,典型的组件测试需要底层数据库支持,甚至可能跨越架构边界)
  27.

      自动化系统测试

      (注:将项目的整体效果进行自动化测试.)
  28.

      自动化功能测试
      (注:将项目的功能进行测试,也就是从客户的视角来测试应用程序,测试将模仿客户的行为。)
  29.

      对开发者测试分类

      (注:对开发者测试的内容进行分类管理.)
  30.

      先执行最快的测试

      (注:首先执行最容易测试的方法)
  31.

      为缺陷编写测试

      (注:为缺陷编写测试用例,不断执行和修复测试,直到测试不再失败为止)
  32.

      让组件测试可重复

      (注:对数据库的测试.会对数据库的信息带来不便.所以得模拟一个测试数据库来进行测试.通过XML中的数据可以实现插入,更新,和删除等操作.配置具体的数据库,为我们测试)
  33.

      将测试用例限制为一个断言(一个test中只能有一个Assert..)

      (注:设置多个断言,任何一个断言失败,都不会执行下面的断言。限制成一个断言,就没什么干扰)
  34.

      降低代码复杂度

      (注:代码复杂:if else等逻辑判断和重复代码等等一般代码复杂圈度通常与缺陷有关,代码复杂度过高会增加程序的风险)
  35.

      持续进行设计复查

      (注:总是查看代码的耦合度.两个测量指标有助于确定过度耦合的情况(传入耦合和传出耦合)反映了一个构架维护问题.按耦合测量指标它会产生XML格式和HTML格式的报告.在情况失控之前加以纠正)
  36.

      通过代码审查维持组织机构的标准

      (注:通过持续地监控和审查代码,您的团队可以保持遵守架构和编码指南。问题可以早发现,常发现,从而避免了长期的维护问题)
  37.

      减少重复的代码

      (注:重复代码带来的问题:1.增加维护成本,因为需要多次发现、报告、分析和修复缺陷。2.不确定是否存在其他缺陷。3.对额外编写的代码增加了测试成本。使用通用的、可复用的、抽象的行为或好的框架解决代码重复问题)
  38.

      判断代码覆盖率

      (注:通常利用测试装备来执行代码,并且记录下测试过程中“接触”到的对应代码的数据.从而获得代码覆盖率.代码覆盖率越高说明你写的代码越优秀)
  39.

      随时随地发布可工作的软件

      (注:在任何时间、任何环境下发布能工作的软件)
  40.

      为库中的资产打上标签

      (注:创建一个版本控制库的标签有利于标识并追踪资产。为版本控制库打上标签,就相当于快照,在最坏的情况下,可以作为回滚的基础。这些标签也使得版本控制库中允许存在并行分支,还提供了处理多条开发线的能力)
  41.

      得到干净的环境

      (注:在测试之前就是说清理上次测试遗留记录或对程序可能影响的因素,避免影响当前程序的正常运行)
  42.

      每一个构建版打上标签

      (注:为每一个构建版创建一个唯一的标识符,即“构建版标签”,就是为了将功能、缺陷或需求与二进制的制品关联起来)
  43.

      执行所有测试

      (注:执行所有自动化测试,从单元测试到功能测试。之后还是要由人进行测试)
  44.

      创建构建反馈报告

      (注:生成自动化构建的反馈信息有助于大家理解要发布的构建版中的确切情况,
      包括构建版中哪些文件不同,修复了哪些缺陷,实现了哪些功能等)
  45.

      回滚构建的过程能力
      (注:能够“撤销”部署是有效的开发中的重要一环。如果需要以前版本取代新的有缺陷的代码,可能因 为以前的版本工作得更好。可通过构建版标签和版本控制库标签,索取想要的版本就可以了)
  46.

      使用持续反馈机制
      (注:使用持续反馈机制,不间断的反馈项目的构建信息给我们。以便我们及时做出处理)



在任何软件开发过程 中都有一个重要的部分: 得到可靠的软件创建(build ) 版本。尽管知道创建的重要性, 但是我们仍然会经常因为创建失败而惊讶不已。在这篇文章里, 我们将讨论Matt ( MatthewFoemmel ) 在ThoughtWorks 的一个重要项目中实施的过程, 这个过程在我们的公司里日益受到重视。它强调完全自动化的、可重复的创建过程, 其中包括每天运行多次的自动化测试。它让开发者可以每天进行系统集成, 从而减少了集成中的问题。
ThoughtWorks 公司已经开放了CruiseControl软件的源代码, 这是一个自动化持续集成的工具。此外, 我们还提供CruiseControl、Ant和持续集成方面的顾问服务。如果需要更多的信息, 请与JoshMackenzie(jmackenz@ThoughtWorks.com) 联系。

本文有以下主要内容:

·  持续集成的优点
·  集成越频繁, 效果越好
·  一次成功的创建是什么样的?
·  单一代码源
·  自动化创建脚本
·  自测试的代码
·  主创建
·  代码归还
·  总结
    在软件开发的领域里有各种各样的“ 最佳实践” , 它们经常被人们谈起, 但是似乎很少有真正得到实现的。这些实践最基本、最有价值的就是: 都有一个完全自动化的创建、测试过程, 让开发团队可以每天多次创建他们的软件。“ 日创建” 也是人们经常讨论的一个观点, McConnell 在他的《快速软件开发》
中将日创建作为一个最佳实践来推荐, 同时日创建也是微软很出名的一项开发方法。但是, 我们更支持XP社群的观点: 日创建只是最低要求。一个完全自动化的过程让你可以每天完成多次创建, 这是可以做到的, 也是完全值得的。
    在这里, 我们使用了“ 持续集成( Continuous Integration) ” 这个术语, 这个术语来自于XP( 极限编程) 的一个实践。但是我们认为: 这个实践早就存在, 并且很多并没有考虑XP 的人也在使用着它。只不过我们一直用XP 作为软件开发过程的标准, XP 也对我们的术语和实践产生了深远的影响。尽管如此,你还是可以只使用持续集成, 而不必使用XP 的任何其他部分— — 实际上, 我们认为: 对于任何切实可行的软件开发活动, 持续集成都是很基本的组成部分。实现自动化日创建需要做以下几部分的工作:
·  将所有的源代码保存在单一的地点, 让所有人都能从这里获取最新的源代码( 以及以前的版本)。
·  使创建过程完全自动化, 让任何人都可以只输入一条命令就完成系统的创建。
·  使测试完全自动化, 让任何人都可以只输入一条命令就运行一套完整的系统测试。
·  确保所有人都可以得到最新、最好的可执行文件。
    所有这些都必须得到制度的保证。我们发现, 向一个项目中引入这些制度需要耗费相当大的精力。但是, 我们也发现, 一旦制度建立起来, 保持它的正常运转就不需要花多少力气了。
持续集成的优点


    描述持续集成最大的难点在于: 它从根本上改变了整个开发模式。如果没有在持续集成的实践环境中工作过, 你很难理解它的开发模式。实际上, 在单独工作的时候, 绝大多数人都能感觉到这种气氛—— 因为他们只需要与自己的系统相集成。对于许多人来说, “ 团队开发” 这个词总让他们想起软件工程领域中的一些难题。持续集成减少了这些难题的数量, 代之以一定的制度。
    持续集成最基本的优点就是: 它完全避免了开发者们的“ 除虫会议” — — 以前开发者们经常需要开这样的会, 因为某个人在工作的时候踩进了别人的领域、影响了别人的代码, 而被影响的人还不知道发生了什么, 于是bug就出现了。这种bug 是最难查的, 因为问题不是出在某一个人的领域里, 而是出在
两个人的交流上面。随着时间的推移, 问题会逐渐恶化。通常, 在集成阶段出现的bug 早在几周甚至几个月之前就已经存在了。结果, 开发者需要在集成阶段耗费大量的时间和精力来寻找这些bug 的根源。
    如果使用持续集成, 这样的bug绝大多数都可以在引入的同一天就被发现。而且, 由于一天之中发生变动的部分并不多, 所以可以很快找到出错的位置。如果找不到bug 究竟在哪里, 你也可以不把这些讨厌的代码集成到产品中去。所以, 即使在最坏的情况下, 你也只是不添加引起bug 的特性而已。( 当
然, 可能你对新特性的要求胜过了对bug 的憎恨, 不过至少你可以多一种选择。)
    到现在为止, 持续集成还不能保证你抓到所有集成时出现的bug。持续集成的排错能力取决于测试技术, 众所周知, 测试无法证明已经找到了所有的错误。关键是在于: 持续集成可以及时抓到足够多的bug,这就已经值回它的开销了。
    所以, 持续集成可以减少集成阶段“ 捉虫” 消耗的时间, 从而最终提高生产力。尽管现在还不知道是否有人对这种方法进行过科学研究, 但是作为一种实践性的方法, 很明显它是相当有效的。持续集成可以大幅减少耗费在“ 集成地狱” 中的时间, 实际上, 它可以把地狱变成小菜一碟。
集成越频繁, 效果越好


    持续集成有一个与直觉相悖的基本要点: 经常性的集成比很少集成要好。对于持续集成的实践者来说, 这是很自然的; 但是对于从未实践过持续集成的人来说, 这是与直观印象相矛盾的。

    如果你的集成不是经常进行的( 少于每天一次) , 那么集成就是一件痛苦的事情, 会耗费你大量的时间与精力。我们经常听见有人说: “ 在一个大型的项目中, 不能应用日创建” , 实际上这是一种十分愚蠢的观点。
    不过, 还是有很多项目实践着持续集成。在一个五十人的团队、二十万行代码的项目中, 我们每天要集成二十多次。微软在上千万行代码的项目中仍然坚持日创建。
    持续集成之所以可行, 原因在于集成的工作量是与两次集成间隔时间的平方成正比的。尽管我们还没有具体的衡量数据, 但是可以大概估计出来: 每周集成一次所需的工作量绝对不是每天集成的5 倍,而是大约25 倍。所以, 如果集成让你感到痛苦, 也许就说明你应该更频繁地进行集成。如果方法正确,更频繁的集成应该能减少你的痛苦, 让你节约大量时间。
    持续集成的关键是自动化。绝大多数的集成都可以而且应该自动完成。读取源代码、编译、连接、测试, 这些都可以自动完成。最后, 你应该得到一条简单的信息, 告诉你这次创建是否成功: “ yes” 或“ no” 。如果成功, 本次集成到此为止; 如果失败, 你应该可以很简单地撤消最后一次的修改, 回到前一次成功的创建。在整个创建过程中, 完全不需要你动脑子。
    如果有了这样一套自动化过程, 你随便想多频繁进行创建都可以。唯一的局限性就是创建过程本身也会消耗一定的时间。( 译注: 不过与捉虫所需的时间比起来, 这点时间是微不足道的。)
持续集成

一次成功的创建是什么样的?


    有一件重要的事需要确定: 怎样的创建才算是成功的? 看上去很简单, 但是如此简单的事情有时却会变得一团糟, 这是值得注意的。有一次, Martin Fowler 去检查一个项目。他问这个项目是否执行日创建, 得到了肯定的回答。幸亏Ron Jeffries 也在场, 他又提了一个问题: “ 你们如何处理创建错误? ” 回答是: “ 我们给相关的人发一个e-mail。” 实际上, 这个项目已经好几个月没有得到成功的创建了。这不是日创建, 这只是日创建的尝试。
   对于下列“ 成功创建” 的标准, 我们还是相当自信的:
   ·  所有最新的源代码都被配置管理系统验证合格
   ·  所有文件都通过重新编译
   ·  得到的目标文件( 在我们这里就是Java 的class 文件) 都通过连接, 得到可执行文件
   ·  系统开始运行, 针对系统的测试套件( 在我们这里大概有150 个测试类) 开始运行
   ·  如果所有的步骤都没有错误、没有人为干涉, 所有的测试也都通过了, 我们就得到了一个成功的创建
   绝大多数人都认为“ 编译+连接=创建” 。至少我们认为: 创建还应该包括启动应用程序、针对应用程序运行简单测试( McConnell 称之为“ 冒烟测试” : 打开开关让软件运行, 看它是否会“ 冒烟” ) 。运行更详尽的测试集可以大大提高持续集成的价值, 所以我们会首选更详尽的测试。
持续集成

单一代码源


    为了实现每日集成, 任何开发者都需要能够很容易地获取全部最新的源代码。以前, 如果要做一次集成, 我们就必须跑遍整个开发中心, 询问每一个程序员有没有新的代码, 然后把这些新代码拷贝过来,再找到合适的插入位置… … 没有什么比这更糟糕的了。
    办法很简单。任何人都应该可以带一台干净的机器过来, 连上局域网, 然后用一条命令就得到所有的源文件, 马上开始系统的创建。
    最简单的解决方案就是: 用一套配置管理( 源代码控制) 系统作为所有代码的来源。配置管理系统通常都设计有网络功能, 并且带有让开发者轻松获取源代码的工具。而且, 它们还提供版本管理工具,这样你可以很轻松地找到文件以前的版本。成本就更不成问题了, CVS 就是一套出色的开放源代码的配置管理工具。
    所有的源文件都应该保存在配置管理系统中。我说的这个“ 所有” 常常比人们想到的还要多, 它还包括创建脚本、属性文件、数据库调度DLL、安装脚本、以及在一台干净的机器上开始创建所需的其他一切东西。经常都能看到这样的情况: 代码得到了控制, 但是其他一些重要的文件却找不到了。
   尽量确保所有的东西都保存在配置管理系统的同一棵代码源树中。有时候为了得到不同的组件, 人们会使用配置管理系统中不同的项目。这带来的麻烦就是: 人们不得不记住哪个组件的哪个版本使用了其他组件的哪些版本。在某些情况下, 你必须将代码源分开, 但是这种情况出现的几率比你想象的要小
得多。你可以在从一棵代码源树创建多个组件, 上面那些问题可以通过创建脚本来解决, 而不必改变存储结构。
持续集成

自动化创建脚本


     如果你编写的是一个小程序, 只有十几个文件, 那么应用程序的创建可能只是一行命令的事: javac*.java。更大的项目就需要更多的创建工作: 你可能把文件放在许多目录里面, 需要确保得到的目标代码都在适当的位置; 除了编译, 可能还有连接的步骤; 你可能还从别的文件中生成了代码, 在编译之前需要先生成; 测试也需要自动运行。
    大规模的创建经常会耗费一些时间, 如果只做了一点小小的改动, 当然你不会希望重新做所有这些步骤。所以好的创建工具会自动分析需要改变的部分, 常见的方法就是检查源文件和目标文件的修改日期, 只有当源文件的修改日期迟于目标文件时, 才会重新编译。于是, 文件之间的依赖就需要一点技巧了: 如果一个目标文件发生了变化, 那么只有那些依赖它的目标文件才会重编译。编译器可能会处理这类事情, 也可能不会。
    取决于自己的需要, 你可以选择不同的创建类型: 你创建的系统可以有测试代码, 也可以没有, 甚至还可以选择不同的测试集; 一些组件可以单独创建。创建脚本应该让你可以根据不同的情况选择不同的创建目标。
    你输入一行简单的命令之后, 帮你挑起这副重担常常是脚本。你使用的可能是shell脚本, 也可能是更复杂的脚本语言( 例如Perl 或Python) 。但是很快你就会发现一个专门设计的创建环境是很有用的,例如Unix下的make工具。
    在我们的Java开发中, 我们很快就发现需要一个更复杂的解决方案。Matt 用了相当多的时间开发了一个用于企业级Java 开发的创建工具, 叫做Jinx。但是, 最近我们已经转而使用开放源代码的创建工具Ant http://jakarta.apache.org/ant/index.html) 。Ant 的设计与Jinx 非常相似, 也支持Java文件编译和Jar封装。同时, 编写Ant 的扩展也很容易, 这让我们可以在创建过程中完成更多的任务。
    许多人都使用IDE, 绝大多数的IDE中都包含了创建管理的功能。但是, 这些文件都依赖于特定的IDE, 而且经常比较脆弱, 而且还需要在IDE 中才能工作。IDE 的用户可以建立自己的项目文件, 并且在自己的单独开发中使用它们。但是我们的主创建过程用Ant 建立, 并且在一台使用Ant的服务器上运行。
持续集成

自测试的代码


    只让程序通过编译还是远远不够的。尽管强类型语言的编译器可以指出许多问题, 但是即使成功通过了编译, 程序中仍然可能留下很多错误。为了帮助跟踪这些错误, 我们非常强调自动化测试— — 这也是XP 提倡的另一个实践。
    XP将测试分为两类: 单元测试和容纳测试( 也叫功能测试) 。单元测试是由开发者自己编写的, 通常只测试一个类或一小组类。容纳测试通常是由客户或外部的测试组在开发者的帮助下编写的, 对整个系统进行端到端的测试。这两种测试我们都会用到, 并且尽量提高测试的自动化程度。
    作为创建的一部分, 我们需要运行一组被称为“ BVT” ( Build Verification Tests, 创建确认测试) 的测试。BVT 中所有的测试都必须通过, 然后我们才能宣布得到了一个成功的创建。所有XP 风格的单元测试都属于BVT。由于本文是关于创建过程的, 所以我们所说的“ 测试” 基本上都是指BVT。请记住,除了BVT 之外, 还有一条测试线存在( 译注: 指功能测试) , 所以不要把BVT 和整体测试、QA等混为一谈。实际上, 我们的QA 小组根本不会看到没有通过BVT 的代码, 因为他们只对成功的创建进行测试。
     有一条基本的原则: 在编写代码的同时, 开发者也应该编写相应的测试。完成任务之后, 他们不但要归还( check in) 产品代码, 而且还要归还这些代码的测试。这也跟XP 的“ 测试第一” 的编程风格很相似: 在编写完相应的测试、并看到测试失败之前, 你不应该编写任何代码。所以, 如果想给系统添加新特性, 你首先应该编写一个测试。只有当新的特性已经实现了以后, 这个测试才可能通过。然后, 你的工作就是让这个测试能够通过。
     我们用Java 编写这些测试, 与开发使用同样的语言, 所以编写测试与编写代码没有太大的区别。我们使用JUnit( http://www.junit.org/) 来作为组织、编写测试的框架。JUnit是一个简单的框架, 让我们可以快速编写测试、将测试组织为套件、并以交互或批处理的模式来运行测试套件。( JUnit 是xUnit家族的Java 版本— — xUnit包括了几乎所有语言的测试框架。)
     在编写软件的过程中, 在每一次的编译之后, 开发者通常都会运行一部分单元测试。这实际上提高了开发者的工作效率, 因为这些单元测试可以帮助你发现代码中的逻辑错误。然后, 你就没必要去调试查错, 只需要注意最后一次运行测试之后修改的代码就行了。这个修改的范围应该很小, 所以寻找bug也就容易多了。
     并非所有的人都严格遵循XP“ 测试第一” 的风格, 但是在第一时间编写测试的好处是显而易见的。它们不但让每个人的工作效率更高, 而且由这些测试构成的BVT 更能捕捉到系统中的错误。因为BVT每天要运行好几次, 所以BVT 检查出的任何问题都是比较容易改正的, 原因很简单: 我们只做了相当小
范围的修改, 所以我们可以在这个范围内寻找bug。在修改过的一小块代码中排错当然比跟踪整个系统来排错要有效多了。
     当然, 你不能指望测试帮你找到所有的问题。就象人们常说的: 测试不能证明系统中不存在错误。但是, 尽善尽美不是我们唯一的要求。不够完美的测试只要经常运行, 也比永远写不出来的“ 完美测试”要好得多。
     另一个相关的问题就是: 开发者们为自己的代码编写测试。我们经常听人说: 开发者不应该测试自己的代码, 因为他们很容易忽视自己工作中的错误。尽管这也是事实, 但是自测试过程需要快速将测试转入代码基础中。这种快速转换的价值超过独立测试者的价值。所以, 我们还是用开发者自己编写的测试来构造BVT, 但是仍然有独立编写的容纳测试。
     自测试另一个很重要的部分就是它通过反馈— — XP 的一项核心价值— — 来提高测试的质量。这里的反馈来自于从BVT 中逃脱的bug。自测试的规则是: 除非你在BVT 中加入了相应的测试, 否则就不能修正任何错误。这样, 每当要修正某个错误的时候, 你都必须添加相应的测试, 以确保BVT 不会再把错误放过去。而且, 这个测试应该引导你去考虑更多的测试、编写更多的测试来加强BVT。
持续集成

主创建


   创建过程的自动化对于单个开发者来说很有意义, 但是它真正发光的, 还是在整个系统的主创建( master build) 的生成。我们发现, 主创建过程能让整个团队走到一起来, 让他们及早发现集成中的问题。
   第一步是要选择运行主创建的机器。我们选择了一台叫做“ 投石车” 的计算机( 我们经常玩“ 帝国时代” J) , 这是一台装有四个CPU的服务器, 非常适合专门用来做创建。( 由于完整的创建需要相当长的时间, 所以这种马力是必须的。)
   创建进程是在一个随时保持运行的Java 类中进行的。如果没有创建任务, 创建进程就一直循环等待,每过几分钟去检查一下代码仓库。如果在最后的创建之后没有人归还任何代码, 进程就继续等待。如果代码仓库中有了新的代码, 就开始创建。
   创建的第一阶段是完全提取仓库中的代码。Starteam已经为我们提供了相当好的Java API, 所以切入代码仓库也很容易。守护进程( daemon) 会观察五分钟以前的仓库, 看最近五分钟里面有没有人归还了代码。如果有, 守护进程就会考虑等五分钟再提取代码( 以免在别人归还代码的过程中提取) 。
   守护进程将全部代码提取到投石机的一个目录中。提取完成之后, 守护进程就会在这个目录里调用Ant脚本。然后, Ant 会接管整个创建过程, 对所有源代码做一次完整的创建。Ant脚本会负责整个编译过程, 并把得到的class文件放进六个jar 包里, 发布到EJB 服务器上。
    当Ant 完成了编译和发布的工作之后, 创建守护进程就会在EJB服务器上开始运行新的jar, 同时开始运行BVT 测试套件。如果所有的测试都能正常运行通过, 我们就得到了一个
分享到:
评论

相关推荐

    持续集成思维导图之自己总结经验

    持续集成 按照&lt;持续集成&gt; 自己总结出来的经验 希望能够帮助需要的人!!也同时,赚个分!嘿嘿

    持续集成学习总结.doc

    持续集成学习总结

    持续集成工具

    总结了当前流行的持续集成工具,有开源和收费的。总结的相当到位

    使用Hudson进行持续集成资料总结

    里面收集了很多关于使用Hudson进行持续集成的资料,方方面面都有些吧~

    持续集成系统的演进之路

    本文分析了持续集成系统的功能点,比较Jenkins,Teamcity这两款持续集成工具的优缺点,最后总结了持续集成的最佳实践要点。阅读对象:研发以及研发管理人员,研发工具研究爱好者持续集成(Continuousintegration),...

    cruisecontrol、ant、svn持续集成

    cruisecontrol、ant、svn持续集成 己两个多星期以来对持续集成的概念和应用有了一些了解。下面主要对自己配置持续集成的环境进行总结。(看上去简单,但是对我开始对持续集成都没什么了解的人来说确实费了不少周折)

    jenkins持续集成sonar扫描代码

    经过几个日夜的奋斗,在linux上总结出jenkins集成sonar的文档,过程中遇到过各种坑。

    初创公司应该如何做好持续集成和部署?

    持续集成和部署是每一个互联网团队都必须要面对的问题,特别是初创公司业务和技术团队快速增长,技术积累较弱的情况下,一个高效的,可遵循持续的运维规范尤为重要,最近一段   持续集成和部署是每一个互联网团队都...

    MartinFowler的持续集成

    本文简要地总结了持续集成技术及其现状。  我还清楚地记得我刚加入一个大型软件项目时的情形,那时我正在英国一个电子公司做暑期实习。我的经理(属于QA部门)领我参观了一个巨大并很压抑的仓库,里面堆满了大块

    XMeter之性能测试持续集成

    随着DevOps理念越来越深入人心,持续集成,持续部署已经成为很多公司技术团队努力追求,且不断完善的目标。在持续集成和持续部署的流程中,自动构建和自动部署一般是技术团队选择优先实现的目标。有了持续交付的流水...

    企业持续集成成熟度模型简介

    (JeffreyFredrick目前是开源持续集成工具CruiseControl的开发和维护者之一)===========================================================当今软件...

    利用CruiseControl软件进行持续集成

    1.认识CruiseControl 2.CruiseControl的安装 3. CruiseControl的配置 4.总结

    持续集成理论和实践的新进展

    最近雷镇同学将MartinFowler先生的著名论文《持续集成》第二版翻译成中文并发布出来,掀起了国内对于持续集成理论和实践讨论的新的高潮。笔者在本文中将全面对比持续集成论文前后两版的异同,分析并展示ThoughtWorks...

    资源前后端分离式分布式微服务架构项目DevOps项目总结讲义+源码+视频

    DevOps介绍 理解 理解DevOps概念 Git/GitLab 应用 能够使用Git管理项目代码 "理解Git与GitLab的作用能够将项目代码...持续集成任务过程"项目总结 记忆 重点 熟练理解并记忆项目的常见题目 本资源系百度网盘分享地址

    GitLab+Jenkins+SonarQube 敏捷开发持续集成环境

    这篇文章是前期工作中用到的技术的总结 绝对的精华 传统的开发模式在西方已经销声匿迹 然而在我们的“环境”中还是一直存在着 but 高瞻远瞩的人已经行动起来 敏捷宣言的出现 拯救了一大批你我一样的程序“猿”

    浅谈持续集成构建在互联网软件测试项目中应用与分析

    在浅谈持续集成及工具在项目中的应用同时,也结合笔者从事互联网软件测试的工作经验,进一步阐述与总结在软件测试过程的持续集成带来的益处与不足。笔者会先介绍当前互联网的软件测试与传统的软件测试区别与联系,...

Global site tag (gtag.js) - Google Analytics