您的位置:首页 > 产品设计 > UI/UE

Continuous Integration using CruiseControl.NET, NANT, P4, and NUnit

2010-01-21 09:13 531 查看
一篇不错的文章:http://www.codeproject.com/KB/architecture/ContinuousIntegration.aspx?msg=1697597#xx1697597xx

用CC.NET主要用来控制代码控制,编译,自动执行UnitTest。

Background

Source control repository to store code in a common location, and allow multiple developers to share a code base (e.g. P4, CVS, Subversion, VSS).

Unit testing framework to run unit tests which are written as part of the code development process, and can flag unintentional changes to code (e.g. NUnit, JUnit).

Automated build tool to allow building from the command line, and to perform related tasks (e.g. Ant, NAnt, MSBuild).

Continuous Integration tool which monitors for code changes and triggers builds (e.g. CruiseControl, Draco).

Flow Control:

Developer commits code changes/additions to CVS/P4.

CruiseControl.NET detects changes.

CruiseControl.NET updates code on build machine.

CruiseControl.NET invokes NAnt script.

NAnt script builds application using devenv.com

NAnt script runs unit tests Final CruiseControl.NET tasks support display of unit testing information.

CruiseControl.NET manages output files for display in its Web Dashboard.

CruiseControl.NET sends emails as appropriate.

主要包括的配置文件C:/Program Files/CruiseControl.NET/server/ccnet.config(一个),default.build(一个Main Build File, 几个Project Level Build File)。

例子如下:

1. ccnet.config :

1)定义一个Main Project Build,以及UnitTest Project,建立二者之间的关系Main Project----->Trigger--->Unit Test Project(比如是有先后依赖的关系)

2)依赖关系例子建立如下:

<triggers>
<multiTrigger operator="Or">
<triggers>
<projectTrigger project="Project1">
<triggerStatus>Success/Failure</triggerStatus>
</projectTrigger>
</triggers>

3)queuePriority数字越小,优先级别越高,所以queuePriority for Main应该小于Test,同时放在两个不同的Queue(Main/Test)

4)针对Unit Test Project,需要在Dashboard显示Unit Test Result(可能有多个Assembly Dll),所以要手动进行Multiple File Merge的配置(文件命名规则要配合default.build的定义)

<merge>
<files>
<!-- these file names need to conform to those found in default.build -->
<file>C:/Perforce/Projects/Project1/Test/UnitTest*.xml</file>
</files>
</merge>

*****************************

<!-- main -->
<project name="Project1" queue="Main" queuePriority="100">
<labeller type="defaultlabeller">
<prefix>2.0.</prefix>
</labeller>
<workingDirectory>C:/Projects/Project1/main</workingDirectory>
<artifactDirectory>E:/CruiseControl.NET/Project1/Artifacts</artifactDirectory>
<webURL>http://XXX/ccnet/server/local/project/Project1/ViewLatestBuildReport.aspx</webURL>
<triggers>
<intervalTrigger name="continuous" seconds="30" buildCondition="IfModificationExists"/>
</triggers>
<!-- CHECK FOR CHANGES IN PERFORCE, EXCLUDE .CHM FILES SINCE THEY ARE CHECKED IN AS PART OF THIS BUILD -->
<sourcecontrol type="filtered">
<sourceControlProvider type="p4">
<view>//Projects/Project1/main...</view>
<client>XXX</client>
<user>test</user>
<password>test</password>
<port>perforce:1666</port>
<applyLabel>false</applyLabel>
<autoGetSource>true</autoGetSource>
<forceSync>false</forceSync>
</sourceControlProvider>
<exclusionFilters>
<pathFilter>
<pattern>/**/*.chm</pattern>
</pathFilter>
</exclusionFilters>
</sourcecontrol>
<tasks>
<!-- RE-BUILD ALL THE CODE -->
<nant>
<executable>C:/Program Files/nant-0.85/bin/nant.exe</executable>
<baseDirectory>C:/Projects/Project1/main</baseDirectory>
</nant>
<buildpublisher>
<sourceDir>C:/Projects/Project1/main/Web</sourceDir>
<publishDir>E:/CruiseControl.NET/Project1/Artifacts</publishDir>
<useLabelSubDirectory>true</useLabelSubDirectory>
</buildpublisher>
</tasks>
<publishers>
<!-- SPECIFY THE PUBLIC FOLDER THAT THE LOGS ARE IN FOR THE WEB DASHBOARD TO USE -->
<xmllogger logDir="C:/Program Files/CruiseControl.NET/webdashboard/buildlogs/Project1"/>
<!--<artifactcleanup cleanUpMethod="KeepLastXBuilds" cleanUpValue="20" />
<artifactcleanup cleanUpMethod="KeepLastXSubDirs" cleanUpValue="20" />-->
<!-- NOTIFY USERS VIA EMAIL -->
<email from="buildresult@test.com" mailhost="localhost" includeDetails="TRUE">
<users>
<user name="test0" group="projectleader" address=test0@test.com/>
<user name="test1" group="buildmaster" address=test1@test.com/>
<user name="test2" group="developers" address=test2@test.com/>
</users>
<groups>
<group name="developers" notification="failed"/>
<group name="buildmaster" notification="always"/>
<group name="projectleader" notification="change"/>
</groups>
</email>
</publishers>
</project>

*****************************

<!-- Test -->
<project name="Project1Test" queue="Test" queuePriority="200">
<labeller type="defaultlabeller">
<prefix>2.0.</prefix>
</labeller>
<workingDirectory>C:/Perforce/Projects/Project1/Test</workingDirectory>
<artifactDirectory>E:/CruiseControl.NET/Project1Test/Artifacts</artifactDirectory>
<webURL>http://XXX/ccnet/server/local/project/Project1Test/ViewLatestBuildReport.aspx</webURL>
<triggers>
<multiTrigger operator="Or">
<triggers>
<projectTrigger project="Main">
<triggerStatus>Success</triggerStatus>
</projectTrigger>
</triggers>
</multiTrigger>
</triggers>
<!-- CHECK FOR CHANGES IN PERFORCE, EXCLUDE .CHM FILES SINCE THEY ARE CHECKED IN AS PART OF THIS BUILD -->
<sourcecontrol type="filtered">
<sourceControlProvider type="p4">
<view>//Projects/Project1/Test...</view>
<client>test</client>
<user>test</user>
<password>test</password>
<port>perforce:1666</port>
<applyLabel>false</applyLabel>
<autoGetSource>true</autoGetSource>
<forceSync>false</forceSync>
</sourceControlProvider>
<exclusionFilters>
<pathFilter>
<pattern>/**/*.chm</pattern>
</pathFilter>
</exclusionFilters>
</sourcecontrol>
<tasks>
<!-- RE-BUILD ALL THE CODE -->
<nant>
<executable>C:/Program Files/nant-0.85/bin/nant.exe</executable>
<baseDirectory>C:/Perforce/Projects/Project1/Test</baseDirectory>
</nant>
<buildpublisher>
<sourceDir>C:/Perforce/Projects/Project1/Test</sourceDir>
<publishDir>E:/CruiseControl.NET/Project1Test/Artifacts</publishDir>
<useLabelSubDirectory>true</useLabelSubDirectory>
</buildpublisher>
</tasks>
<publishers>
<merge>
<files>
<!-- these file names need to conform to those found in default.build -->
<file>C:/Perforce/Projects/Project1/Test/UnitTest*.xml</file>
</files>
</merge>
<!-- SPECIFY THE PUBLIC FOLDER THAT THE LOGS ARE IN FOR THE WEB DASHBOARD TO USE -->
<xmllogger logDir="C:/Program Files/CruiseControl.NET/webdashboard/buildlogs/Project1Test"/>
<!-- NOTIFY USERS VIA EMAIL -->
<email from="buildresult@test.com" mailhost="localhost" includeDetails="TRUE">
<users>
<user name="test0" group="projectleader" address=test0@test.com/>
<user name="test1" group="buildmaster" address=test1@test.com/>
<user name="test2" group="developers" address=test2@test.com/>
</users>
<groups>
<group name="developers" notification="failed"/>
<group name="buildmaster" notification="always"/>
<group name="projectleader" notification="change"/>
</groups>
</email>
</publishers>
</project>

2. Main Project default.build(包括Main以及Sub Projects)

1)Main Build File(C:/Perforce/Projects/Project1/main/default.build)

按照inclue name进行先后编译,所以入排列时要注意顺序的。

********************************

<?xml version="1.0" encoding="utf-8"?>

<project name="Project1" default="build">
<target name="build">
<nant target="build">
<buildfiles>
<include name="sub-project1/default.build" />
<include name="sub-project2/default.build" />
<!--more project builds can be added here -->
</buildfiles>
</nant>
<call target="deploy" />
</target>

<target name="deploy">
<copy todir="Web/bin" overwrite="true">
<fileset basedir="ReportUtility/bin/">
<include name="*.dll"/>
</fileset>
</copy>
</target>
</project>

2) Sub Project Build File(C:/Perforce/Projects/Project1/main/sub-project1/default.build)

如果对于Satelite Assembly,需要特别注意(比如多语言的资源文件会生成多个,bin/pt-PT/***.resources.dll, bin/jp-JP/***.resources.dll).

<resources dynamicprefix="true">
<include name="App_GlobalResources/Strings.resx"/>
<include name="App_GlobalResources/Strings.pt-PT.resx"/>
</resources>

*****************************

<?xml version="1.0"?>
<project name="Project1BusinessLayer" default="build">
<property name="basename" value="SubProject1.Assembly"/>
<property name="debug" value="true"/>
<property name="uptodate" value="true"/>

<target name="clean">
<delete>
<fileset>
<include name="bin/${basename}.dll"/>
<include name="bin/${basename}.pdb"/>
</fileset>
</delete>
</target>

<target name="build">
<uptodate property="uptodate">
<sourcefiles>
<include name="*.cs"/>
</sourcefiles>
<targetfiles>
<include name="bin/${basename}.dll"/>
</targetfiles>
</uptodate>

<!--

<mkdir dir="bin" />
<csc target="library" output="bin/${basename}.dll" debug="${debug}">
<sources>
<include name="*.cs"/>
<exclude name="AssemblyInfo.cs"/>
<if test="${property::exists('CCNetLabel') and not uptodate}">--> //避免该组件的版本号码有时与整个Solution的不一致,导致组件升级,用户Download时发生错误,特别是SmartClient的情况。

<asminfo output="FMAssemblyInfo.cs" language="CSharp">
<imports>
<import namespace="System" />
<import namespace="System.Reflection" />
<import namespace="System.Runtime.InteropServices" />
</imports>
<attributes>
<attribute type="ComVisibleAttribute" value="false" />
<attribute type="AssemblyVersionAttribute" value="${CCNetLabel}" />
<attribute type="AssemblyTitleAttribute" value="${basename}" />
<attribute type="AssemblyDescriptionAttribute" value="Project1" />
<attribute type="AssemblyCopyrightAttribute" value="Copyright © Test 2007" />
</attributes>
</asminfo>
<!--

</if>-->

<include name="Properties/*.cs"/>
</sources>

<resources dynamicprefix="true">
<include name="Resource.pt-PT.resx"/>
<include name="Resource.resx"/>
</resources>
<references>
<include name="../CommonDlls/*.dll"/>
<include name="C:/Program Files/log4net-1.2.10/*.dll"/>
</references>
</csc>
</target>
</project>

*****************************

一个Smart Client的Publish 的脚本例子

<?xml version="1.0"?>
<project name="SmartClient1" default="build">
<property name="basename" value="SmartClient1"/>
<property name="debug" value="true"/>
<property name="date.now" value="${ datetime::now() }"/>
<property name="uptodate" value="true"/>
<property name="publishdir" value="C:/Perforce/Projects/Project1/main/Web/SmartClient1/" />
<property name="log4netDir" value="C:/Program Files/log4net-1.2.10/" />
<property name="projectname" value="C:/Perforce/Projects/Project1/main/SmartClient1/SmartClient1.csproj" />
<target name="clean">
<delete>
<fileset>
<include name="bin/${basename}.exe"/>
<include name="bin/${basename}.pdb"/>
</fileset>
</delete>
</target>

<target name="build">
<uptodate property="uptodate">
<sourcefiles>
<include name="*.cs"/>
</sourcefiles>
<targetfiles>
<include name="bin/${basename}.exe"/>
</targetfiles>
</uptodate>

<!--

<if test="${property::exists('CCNetLabel') and not uptodate}">-->

<delete file="Properties/AssemblyInfo.cs"></delete>
<asminfo output="Properties/AssemblyInfo.cs" language="CSharp">
<imports>
<import namespace="System" />
<import namespace="System.Reflection" />
<import namespace="System.Runtime.InteropServices" />
</imports>
<attributes>
<attribute type="ComVisibleAttribute" value="false" />
<attribute type="AssemblyVersionAttribute" value="${CCNetLabel}" />
<attribute type="AssemblyTitleAttribute" value="SmartClient1" />
<attribute type="AssemblyDescriptionAttribute" value="SmartClient1" />
<attribute type="AssemblyCopyrightAttribute" value="Copyright © test" />
<attribute type="AssemblyCompanyAttribute" value="Test" />
<attribute type="AssemblyProductAttribute" value="SmartClient1" />
</attributes>
</asminfo>
<!--

</if>-->
<mkdir dir="bin" />
<csc target="exe" output="bin/${basename}.exe" debug="${debug}">
<sources>
<include name="*.cs"/>
<include name="Web References/WS/*.cs"/>
<include name="Properties/*.cs"/>
<include name="UserControls/*.cs"/>
<exclude name="AssemblyInfo.cs"/>
</sources>
<resources dynamicprefix="true">
<include name="Resource.pt-PT.resx"/>
<include name="Resource.resx"/>
</resources>
<references>
<include name="C:/Program Files/log4net-1.2.10/*.dll"/>
<include name="../CommonDlls/*.dll"/>
</references>
</csc>
<delete dir="C:/Perforce/Projects/Project1/main/Web/SmartClient1/" />
<!--take backup of config-->
<copy todir="config_backup" overwrite="true">
<fileset basedir=".">
<include name="*.config"/>
</fileset>
</copy>
<msbuild target="Publish" project="${projectname}">
<property name="PublishDir" value="${publishdir}/UnitTest/" />
<property name="InstallUrl" value="http://localhost/Project1/SmartClient1/" />
<property name="UpdateUrl" value="http://localhost/Project1/SmartClient1/" />
<property name="ProductName" value="SmartClient1" />
<property name="AssemblyName" value="SmartClient1" />
</msbuild>
<!--take backup of bin for NUnit test-->
<copy todir="bin_backup" includeemptydirs="true" overwrite="true" verbose="true">
<fileset basedir="./bin">
<include name="**/*.*"/>
</fileset>
</copy>
<!--For test server-->
<move file="app_test.config" tofile="app.config" overwrite="true" />
<!-- Delete bin and obj directories -->
<delete dir="bin" />
<delete dir="obj" />
<copy todir="bin" overwrite="true">
<fileset basedir="${log4netDir}">
<include name="*.dll"/>
</fileset>
</copy>
<copy todir="bin" overwrite="true">
<fileset basedir="../CommonDlls/ZipUtility/">
<include name="*.dll"/>
</fileset>
</copy>
<msbuild target="Publish" project="${projectname}">
<property name="PublishDir" value="${publishdir}/Test/" />
<property name="InstallUrl" value="http://TestServer/Project1/SmartClient1/" />
<property name="UpdateUrl" value="http://TestServer/Project1/SmartClient1/" />
<property name="ProductName" value="SmartClient1" />
<property name="AssemblyName" value="SmartClient1" />
</msbuild>
<!--For staging server-->
<move file="app_staging.config" tofile="app.config" overwrite="true" />
<!-- Delete bin and obj directories -->
<delete dir="bin" />
<delete dir="obj" />
<copy todir="bin" overwrite="true">
<fileset basedir="${log4netDir}">
<include name="*.dll"/>
</fileset>
</copy>
<copy todir="bin" overwrite="true">
<fileset basedir="../CommonDlls/ZipUtility/">
<include name="*.dll"/>
</fileset>
</copy>
<msbuild target="Publish" project="${projectname}">
<property name="PublishDir" value="${publishdir}/Staging/" />
<property name="InstallUrl" value="http://www.test.com/Project1/SmartClient1/" />
<property name="UpdateUrl" value="http://www.test.com/Project1/SmartClient1/" />
<property name="ProductName" value="SmartClient1" />
<property name="AssemblyName" value="SmartClient1" />
</msbuild>
<!--For production server-->
<move file="app_production.config" tofile="app.config" overwrite="true" />
<!-- Delete bin and obj directories -->
<delete dir="bin" />
<delete dir="obj" />
<copy todir="bin" overwrite="true">
<fileset basedir="${log4netDir}">
<include name="*.dll"/>
</fileset>
</copy>
<copy todir="bin" overwrite="true">
<fileset basedir="../CommonDlls/ZipUtility/">
<include name="*.dll"/>
</fileset>
</copy>
<msbuild target="Publish" project="${projectname}">
<property name="PublishDir" value="${publishdir}/Production/" />
<property name="InstallUrl" value="https://www.production.com/Project1/SmartClient1/" />
<property name="UpdateUrl" value="https://www.production.com/Project1/SmartClient1/" />
<property name="ProductName" value="SmartClient1" />
<property name="AssemblyName" value="SmartClient1" />
</msbuild>
<!--For training server-->
<move file="app_training.config" tofile="app.config" overwrite="true" />
<!-- Delete bin and obj directories -->
<delete dir="bin" />
<delete dir="obj" />
<copy todir="bin" overwrite="true">
<fileset basedir="${log4netDir}">
<include name="*.dll"/>
</fileset>
</copy>
<copy todir="bin" overwrite="true">
<fileset basedir="../CommonDlls/ZipUtility/">
<include name="*.dll"/>
</fileset>
</copy>
<msbuild target="Publish" project="${projectname}">
<property name="PublishDir" value="${publishdir}/Training/" />
<property name="InstallUrl" value="https://www.training.com/Project1/SmartClient1/" />
<property name="UpdateUrl" value="https://www.training.com/Project1/SmartClient1/" />
<property name="ProductName" value="SmartClient1" />
<property name="AssemblyName" value="SmartClient1" />
</msbuild>
<!-- Delete bin and obj directories --><!--
<delete dir="bin" />
<delete dir="obj" />-->
<!--Create Version label file-->
<copy file="Version.txt" tofile="${publishdir}/Version-${CCNetLabel}.0.txt" overwrite="true"/>
<!--restore backup of bin,obj--><!--
<copy todir="bin" overwrite="true">
<fileset basedir="./bin_backup">
<include name="*.*"/>
</fileset>
</copy>
<copy todir="obj" overwrite="true">
<fileset basedir="./obj_backup">
<include name="*.*"/>
</fileset>
</copy>-->
<!--restore backup of config-->
<copy todir="." overwrite="true">
<fileset basedir="./config_backup">
<include name="*.config"/>
</fileset>
</copy>
</target>
</project>

3. Unit Test Project default.build(包括Main以及Sub Projects)

Main Build (添加几个测试的Assembly Dll)

记得删除之前生成的Unit test result哦。。。。

**********************************

<?xml version="1.0" encoding="utf-8"?>

<project name="Project1Test" default="build">
<target name="build">
<nant target="build">
<buildfiles>
<include name="TestAppCode/default.build" />
<include name="TestBusinessLayer/default.build" />
</buildfiles>
</nant>
<call target="clean" />
<call target="test" />
</target>
<target name="clean" description="remove recent test results. ">
<delete failonerror="false">
<!-- this prevents unit test results from appearing in cc console if the build fails -->
<fileset>
<include name="UnitTest-*" />
</fileset>
</delete>
</target>
<target name="test" description="runs the unit tests" >
<!-- test first Assembly. -->
<exec program="C:/Program Files/NUnit-Net-2.0 2.2.8/bin/nunit-console.exe" failonerror="false" resultproperty="testresult.AppCodeTest.dll">
<arg value="TestAppCode/bin/TestAppCode.dll" />
<arg value="/xml=UnitTest-AppCodeTest.xml" />
</exec>

<!-- test second Assembly. -->
<exec program="C:/Program Files/NUnit-Net-2.0 2.2.8/bin/nunit-console.exe" failonerror="false" resultproperty="testresult.BusinessLayerTest.dll">
<arg value="TestBusinessLayer/bin/TestBusinessLayer.dll" />
<arg value="/xml=UnitTest-BusinessLayerTest.xml" /> 。。。。文件名称有规则哦。。。。
</exec>

<!-- Check the results properties and fail if necessary -->

Unit test的结果会影响到整个Project(DashBoard)的成功,初期暂时注释掉。等调试好所有Case之后,在打开。
<!--<fail message="Failures reported in unit tests." unless="${int::parse(testresult.AppCodeTest.dll)==0}" />
<fail message="Failures reported in unit tests." unless="${int::parse(testresult.BusinessLayerTest.dll)==0}" />-->
</target>
</project>

**********************************

Sub Project Build File

<?xml version="1.0"?>
<project name="TestBusinessLayer" default="build">
<property name="basename" value="TestBusinessLayer"/>
<property name="debug" value="true"/>
<property name="uptodate" value="true"/>

<target name="clean">
<delete>
<fileset>
<include name="bin/${basename}.dll"/>
<include name="bin/${basename}.pdb"/>
</fileset>
</delete>
</target>

<target name="build">
<uptodate property="uptodate">
<sourcefiles>
<include name="*.cs"/>
</sourcefiles>
<targetfiles>
<include name="bin/${basename}.dll"/>
</targetfiles>
</uptodate>
<if test="${property::exists('CCNetLabel') and not uptodate}">
<asminfo output="FMAssemblyInfo.cs" language="CSharp">
<imports>
<import namespace="System" />
<import namespace="System.Reflection" />
<import namespace="System.Runtime.InteropServices" />
</imports>
<attributes>
<attribute type="ComVisibleAttribute" value="false" />
<attribute type="AssemblyVersionAttribute" value="${CCNetLabel}" />
<attribute type="AssemblyTitleAttribute" value="${basename}" />
<attribute type="AssemblyDescriptionAttribute" value="BusinessLayer Test" />
<attribute type="AssemblyCopyrightAttribute" value="Copyright © test" />
</attributes>
</asminfo>
</if>
<mkdir dir="bin" />
<csc target="library" output="bin/${basename}.dll" debug="${debug}">
<sources>
<include name="*.cs"/>
<include name="Properties/*.cs"/>
<exclude name="AssemblyInfo.cs"/>
<exclude name="FMAssemblyInfo.cs"/>
</sources>
<references>
<include name="C:/Program Files/NUnit-Net-2.0 2.2.8/bin/*.dll"/>
<include name="../../main/CommonDlls/*.dll"/>
<include name="../../main/BusinessLayer/bin/SummitBusinessLayer.dll"/>
</references>
</csc>
</target>
</project>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐