当前位置: 首页 > news >正文

浅谈持续集成CC

  最近一段时间一直对CI工具非常关注,正好前两天终于有点时间,把这个好好用了一下,所以写了这篇文档。最新版本为3.0,可以从http://cruisecontrol.sourceforge.net/下载.

  1. 要想用得先下载,地址就不提了,占地儿。我下载的是最新版本cruisecontrol-bin-2.3.1.zip,如果不是研究的话,建议最好下载一个bin包,这样能省去很多麻烦。解压后我们能看到一些目录。

    最新版本cruisecontrol-bin-2.3.1.zip

      其中CruiseControl(以下简称CC)自带ant1.6.3;文档在docs目录下,这里面包括config.xml的相关的参数设置说明;logs下面包括日志信息,可以通过在config.xml中指定日志路径和名称;projects下面放的是需要进行持续集成的项目,jarkarta-commons是使用apache的math项目作例子;lib目录中放有cruisecontrol.jar和其他运行需要的jar;webapps下是cruisecontrol build结果的网站,可以通过访问 http://127.0.0.1:8080/cruisecontrol 来查看build的结果;在build后会产生artifacts目录,这个目录用来存放发布的工件。

  2. 使用cruisecontrol前的准备工作
    1. 建立CVS管理项目源码

        建议最好使用CVS管理源码,好处在这里就勿需多言了。具体创建的过程可以参考相关的《CVS使用手册》,这里需要注意的是module的设置,可以参照相应的文档来设置。

    2. 设置ANT环境变量

        ANT的使用也不是本篇所要阐述的,在这里只是说一些可能碰到的情况。在按照ANT的使用指南配置好后,可以在其他环境下试一下是否好用,如果你的JDK比较新的话最好也用比较新的ant,这里使用的是CC自带的ant,本文的环境是JDK5.0+Ant1.6.3+Eclipse3.1+Tomcat5.5,OS:WinXP SP2

    3. 编写项目的build.xml

        接下来用需要编写的是ant的build.xml文件,在本文中,我编写了这样的一个例子,为了便于理解,target都采用了中文,接下来我们会看到。另外,在编写build文件的时候,我还发现一个问题,我编写的build文件怎么也不好用,只要一Run as ant,就会报异常,而在命令提示符下运行ant则没有这个问题,经过一段时间检查,发现原来是Eclipse所带的ant版本比较低,后来下载新的ant后没有更新eclipse的ant目录,需要进行如下的设置:

      编写项目的build.xml

    4. Checkout项目到CC_HOME/projects目录

        配置好CVSROOT后,在CC_HOME/projects下执行$ cvs checkout module_name 具体的配置方法在这里不说了。这是需要手工作的,否则CC是不会自动监测变更的。

    5. 修改config.xml

        根据实际项目的情况修改config.xml的情况,我们下面会用一个例子来说明。

  3. 运行cruisecontrol

      在作完上面的工作后,我们就可以启动CC了,如果是在unix平台下需要执行

      $ CC_HOME/ cruisecontrol.sh ;如果是在windows平台下执行cruisecontrol.bat,成功后会出现如下的界面:

      屏幕上出现Project commons-math started和BuildQueue started提示就说明已经正确的执行了CC。

    运行cruisecontrol

      在执行后,我们可以通过IE查看build情况,界面如下:

      下面显示的是build的结果,现在的结果是BUILD FAILED,因为写这篇文档时网络不通,所以无法连接到apache上获得程序进行编译,就出现了错误。

    通过IE查看build情况

    通过IE查看build情况

      我们可以看到,CC还提供了RSS订阅的功能,借助RSS阅读器,我们可以方便的获得最新的构建信息。界面如下:

    可以方便的获得最新的构建信息

      好,我们见过了CC的使用过程,下面让我们通过一个例子来实现这个过程。

  4. 例子

      在这个例子中,我们新建了一个项目,项目的名字叫hello,项目的结构如下:

    例子

      main/src/java/com/test/qik/HelloMaven.java:示例的程序

    package com.test.qik;
    
    public class HelloMaven {
    	
    	public static boolean main(String[] args) {
    		System.out.println("Hello, maven!");
    		return true;
    	}
    	
    	public void testa() throws Exception {
    		
    		System.out.println("test,可以发布到生产库的版本");
    		
    	}
    
    }
    main/test/java/com/test/qik/HelloMavenTest.java:示例程序的单元测试用例
    package com.test.qik;
    
    import junit.framework.TestCase;
    
    public class HelloMavenTest extends TestCase {
    
    	public static void main(String[] args) {
    	}
    
    	public HelloMavenTest(String arg0) {
    		super(arg0);
    	}
    
    	protected void setUp() throws Exception {
    		super.setUp();
    	}
    
    	protected void tearDown() throws Exception {
    		super.tearDown();
    	}
    
    	/*
    	 * Test method for 'com.test.qik.HelloMaven.main(String[])'
    	 */
    	public void testMain() {
    		HelloMaven hm = new HelloMaven();
    		assertEquals(hm.main(null),true);
    	}
    
    }
    target/:输出路径
    build.xml:ant的构建文件
    文件内容如下:
    ==============================================================
    
    <?xml version="1.0" encoding="UTF-8"?>
    
    
    <project default="CI" name="hello" basedir=".">
    	<!--设置参数 -->
    	<property name="项目中文名称" value="测试cc项目"></property>
    	<property name="project_folde"
    		value="E:\cruisecontrol-2.3.1\projects\hello">
    	</property>
    
    	<property name="源程序目录" value="${project_folder}\main\src"></property>
    	<property name="测试程序目录" value="${project_folder}\main\test"></property>
    	<property name="编译程序目录" value="${project_folder}\target\classes"></property>
    	<property name="lib目录" value="${project_folder}\target\lib"></property>
    
    	<property name="单元测试报告目录"
    		value="E:\cruisecontrol-2.3.1\projects\hello\test-reports">
    	</property>
    	<property name="单元测试报告文件名" value="junit-noframes.html"></property>
    
    	<property name="CVS用户名" value="administrator"></property>
    	<property name="CVS密码" value="patterns"></property>
    	<property name="CVS地址" value="127.0.0.1"></property>
    	<property name="CVS仓库" value="/cvsserver"></property>
    	<property name="CVS模块" value="hello"></property>
    	<property name="CVSLib模块" value="target"></property>
    	<property name="CVS连接符"
    		value=":pserver:${CVS用户名}:${CVS密码}@${CVS地址}:2401:${CVS仓库}">
    	</property>
    	<property name="CVS检出目录" value="E:\cruisecontrol-2.3.1\projects"></property>
    
    	<property name="邮件服务器地址" value="smtp.xxxxx.com"></property>
    	<property name="发送邮件用户" value="xxx"></property>
    	<property name="发送邮件密码" value="xxxxxx"></property><!—这里应该输入明文的用户名和密码,示例中不输入-->
    	<property name="接受报告用户邮件列表" value="xxx@xxxx.com"></property>
    	<property name="发送用户邮箱地址" value="xxx@xxxx.com"></property>
    
    	<property name="生成jar文件"
    		value="E:\cruisecontrol-2.3.1\projects\hello\target\hello.jar">
    	</property>
    	<property name="生成jar文件的基础路径"
    		value="E:\cruisecontrol-2.3.1\projects\hello\target\classes">
    	</property>
    
    	<!--持续集成流程 -->
    	<target name="CI" depends="初始化,获取源码,编译源码,运行测试,生成jar" />
    
    	<!--1.初始化目标目录,将目标目录清空,创建源程序目录,测试程序目录和class目录 -->
    	<target name="初始化">
    		<echo>正在删除源程序目录...</echo>
    		<delete dir="${源程序目录}" />
    		<echo>正在创建源程序目录...</echo>
    		<mkdir dir="${源程序目录}" />
    		<echo>正在删除测试程序目录...</echo>
    		<delete dir="${测试程序目录}" />
    		<echo>正在创建测试程序目录...</echo>
    		<mkdir dir="${测试程序目录}" />
    		<echo>正在删除编译程序目录...</echo>
    		<delete dir="${编译程序目录}" />
    		<echo>正在创建编译程序目录...</echo>
    		<mkdir dir="${编译程序目录}" />
    		<echo>正在删除lib目录...</echo>
    		<delete dir="${lib目录}" />
    		<echo>正在创建lib目录...</echo>
    		<mkdir dir="${lib目录}" />
    		<echo>正在删除单元测试报告目录...</echo>
    		<delete dir="${单元测试报告目录}" />
    		<echo>正在创建单元测试报告目录...</echo>
    		<mkdir dir="${单元测试报告目录}" />
    	</target>
    	<!--2.从cvs中获取程序源码 -->
    
    	<target name="获取源码" depends="">
    		<echo>从CVS中获取源码...</echo>
    		<cvs cvsRoot="${CVS连接符}" command="export -f -r HEAD ${CVS模块}"
    			dest="${CVS检出目录}" />
    	</target>
    	<target name="获取lib">
    		<echo>从CVS中获取lib目录...</echo>
    		<cvs cvsRoot="${CVS连接符}" command="export -f -r HEAD ${CVSLib模块}"
    			dest="${CVS检出目录}" />
    	</target>
    	<!--3.编译程序生成目标类 -->
    	<target name="编译源码" depends="">
    		<echo>编译程序...</echo>
    		<javac classpathref="编译路径" fork="true" memorymaximumsize="128m"
    			destdir="${编译程序目录}" debug="true" deprecation="false"
    			failοnerrοr="false" verbose="false">
    			<src path="${源程序目录}" />
    			<src path="${测试程序目录}" />
    			<include name="**/*.java" />
    		</javac>
    	</target>
    	<!--4.运行JUnit测试 -->
    	<target name="运行测试" depends="" description="执行单元测试">
    		<echo>运行单元测试用例...</echo>
    		<junit>
    			<classpath refid="单元测试时路径" />
    			<formatter type="xml" />
    			<batchtest todir="${单元测试报告目录}">
    				<fileset dir="${编译程序目录}">
    					<exclude name="**/TestTools.class" />
    					<include name="**/**Test**.class" />
    				</fileset>
    			</batchtest>
    		</junit>
    		<echo>生成单元测试报告...</echo>
    		<junitreport todir="${单元测试报告目录}" description="生成单元测试报告">
    			<fileset dir="${单元测试报告目录}">
    				<include name="TEST-*.xml" />
    			</fileset>
    			<report format="noframes" todir="${单元测试报告目录}" />
    		</junitreport>
    	</target>
    	<!--5.发邮件 -->
    	<target name="发送测试报告邮件" depends="">
    		<echo>发送测试报告邮件...</echo>
    		<mail mailhost="${邮件服务器地址}"
    			subject="单元测试报告 : ${项目中文名称} - ${DSTAMP}" user="${发送邮件用户}"
    			password="${发送邮件密码}" tolist="${接受报告用户邮件列表}"
    			messagefile="${单元测试报告目录}\${单元测试报告文件名}">
    			<from address="${发送用户邮箱地址}" />
    			<fileset file="${单元测试报告目录}\${单元测试报告文件名}" />
    		</mail>
    	</target>
    	<!--6.打包成jar文件 -->
    	<target name="生成jar" depends="">
    		<echo>打包成jar文件...</echo>
    		<jar destfile="${生成jar文件}" basedir="${生成jar文件的基础路径}" />
    	</target>
    	<!--编译过程中用到的路径 -->
    	<path id="编译路径">
    		<pathelement path="${编译程序目录}" />
    		<path refid="编译时lib路径" />
    	</path>
    	<path id="编译时lib路径">
    		<fileset dir="${lib目录}">
    			<include name="**/*.jar" />
    		</fileset>
    	</path>
    	<!--单元测试时用到的路径 -->
    	<path id="单元测试时路径">
    		<path refid="编译时lib路径" />
    		<pathelement path="${编译程序目录}" />
    	</path>
    
    </project>
    ==============================================================
    对config.xml设置的内容
    <cruisecontrol>
      <project name="hello">
       
       <listeners>
         <currentbuildstatuslistener file="logs/hello/status.txt" />
       </listeners>
       
       <bootstrappers>
         <cvsbootstrapper localWorkingCopy="projects/hello" />
       </bootstrappers>
       <!—监控本地工作目录,如果在下面的目录中发生变化则自动执行下面的build.xml,没有变化则不再执行下面的构建工作 -->
       <modificationset quietperiod="30" > <!—监控前的静默 -->
         <cvs localWorkingCopy="projects/hello"/>
         <cvs localWorkingCopy="projects/hello/main/src/java/com/test/qik"/>
       </modificationset>
       <!—计划时间间隔,单位秒 -->
       <schedule interval="30" >
         <ant anthome="apache-ant-1.6.3" 
           buildfile="projects/hello/build.xml"
           target="CI"/><!—构建时执行的build.xml -->
       </schedule>
       
       <log>
       	<merge dir="projects/hello/test-reports"/>
       </log><!—记载日志报告的位置 -->
       
       <publishers>
       	<artifactspublisher dest="artifacts" file="projects/hello/target/hello.jar"/>
       </publishers><!—发布jar包的位置,我都是在build.xml中将jar包打好放在这个位置 -->
       
      </project>
    </cruisecontrol>
    CC_HOME/docs/main/configxml.html这个文件是config.xml的帮助内容,我们可以从这里面找到所有的配置。
    
  5. 总结

      我们可以看到整个过程还是很简单的,通过CC,我们可以部分减轻CI的工作量,但可以看到核心的构建工作还是在project的build.xml中,如果CC能提供常见的模版就更好了(呵呵,我还是想偷懒)。另外CC的监控本地工作目录的功能有些弱智,比如我只写了"projects/hello",则它只认为在”project/hello”目录下的文件有更新才启动build过程,如果我们更新的是下面的子目录,则不能自动监控。

相关文章:

  • java架构和iis_防止IIS文件被下载方法
  • java数字循环求最小值_从键盘上接收一些数字,比较输出中的最大值和最小值,输入0结束循环。java写出来...
  • 持续集成工具CC介绍
  • java随机数_Java随机数Random()
  • Delphi调用WMI读取USB设备的PID和VID
  • java 计算器程序_java计算器程序
  • Log4j被OSGI撞了一下腰
  • 比较分析Vector、ArrayList和hashtable hashmap数据结构
  • java二进制类型_Java数据类型 - 原始和二进制文字
  • hibernate的Criteria的一个bug
  • toad导出mysql数据库_将toad里的数据库结构导出到pdm
  • 炒股精髓:多位高手多年心血结晶
  • 好神奇哟
  • java linux 压缩文件夹_Linux下压缩某个文件夹(文件夹打包)
  • java中mapper层作用_Java的MyBatis框架中Mapper映射配置的使用及原理解析
  • 【译】JS基础算法脚本:字符串结尾
  • java中的hashCode
  • js算法-归并排序(merge_sort)
  • Linux各目录及每个目录的详细介绍
  • Lucene解析 - 基本概念
  • Mysql优化
  • Python打包系统简单入门
  • Swift 中的尾递归和蹦床
  • UEditor初始化失败(实例已存在,但视图未渲染出来,单页化)
  • 案例分享〡三拾众筹持续交付开发流程支撑创新业务
  • 半理解系列--Promise的进化史
  • 深度学习中的信息论知识详解
  • 网络应用优化——时延与带宽
  • 问题之ssh中Host key verification failed的解决
  • 写代码的正确姿势
  • 一个SAP顾问在美国的这些年
  • ​ ​Redis(五)主从复制:主从模式介绍、配置、拓扑(一主一从结构、一主多从结构、树形主从结构)、原理(复制过程、​​​​​​​数据同步psync)、总结
  • !!Dom4j 学习笔记
  • #我与Java虚拟机的故事#连载13:有这本书就够了
  • (1)(1.9) MSP (version 4.2)
  • (4)通过调用hadoop的java api实现本地文件上传到hadoop文件系统上
  • (pojstep1.1.1)poj 1298(直叙式模拟)
  • (每日持续更新)jdk api之StringBufferInputStream基础、应用、实战
  • (算法)Game
  • (一)pytest自动化测试框架之生成测试报告(mac系统)
  • (转)iOS字体
  • ***汇编语言 实验16 编写包含多个功能子程序的中断例程
  • ***检测工具之RKHunter AIDE
  • ./mysql.server: 没有那个文件或目录_Linux下安装MySQL出现“ls: /var/lib/mysql/*.pid: 没有那个文件或目录”...
  • .NET Compact Framework 3.5 支持 WCF 的子集
  • .NET Core、DNX、DNU、DNVM、MVC6学习资料
  • .NET Project Open Day(2011.11.13)
  • .NET 动态调用WebService + WSE + UsernameToken
  • .NET/C# 使用 SpanT 为字符串处理提升性能
  • .net解析传过来的xml_DOM4J解析XML文件
  • /dev/sda2 is mounted; will not make a filesystem here!
  • @GetMapping和@RequestMapping的区别
  • @ModelAttribute使用详解
  • @RunWith注解作用
  • @Transactional 详解