持续集成想必大家很多人都听说过,甚至都实践过,最近我又一次亲历了一次持续集成,现将我的经验分享给大家。关于持续集成的理论在本文概不涉及,本文的主要目的是实战CruiseControl.Net,用它来全面实现持续集成。
在配置ccnet.config时会用到一些小工具,一并附上:小工具下载
首先,我们来看看用CC.Net能为我们做哪些事情:
在开始之前,第一件事是配置我们的持续集成环境
获取SVN或TFS工具,TFS可通过安装VisualStudio获得,SVN可通过安装TortoiseSVN来获得。
安装和配置IIS,因为我们要使用Web页面查看持续集成的结果,所以需要配置IIS,安装CruiseControl.Net时会为我们创建一个名为ccnet的web应用程序。
从http://www.cruisecontrolnet.org/这个站点上现在我们需要的工具CruiseControl.Net并将其安装。
安装VisualStudio2013(当然也可选其它版本)。安装它的目的有两个,1. 使用了VSTest.Console.exe产生单元测试结果数据(UnitTest结果和测试覆盖率),2. 当编译不能通过时用它可以发现问题。
到此为止,集成环境已经OK,下面,我们来逐一来通过配置ccnet.config实现上述功能。
1.自动获取源代码
首先需要了解,持续集成的单位是以项目为单位,在ccnet.config文件里体现为Project,如下:
<project name=“MyProject” description=“demoproject showing a small config” queue=“Q1”>
</project>
View Code
配置项目的源代地址,包括本地工作地址和源代码管理服务地址,对于使用TFS的源码管理器,向Project下添加如下配置:
<workingDirectory>E:\dailybuild</workingDirectory>
<artifactDirectory>E:\dailybuild</artifactDirectory>
<category>TestProject</category>
<sourcecontrol type=“vsts” autoGetSource=“true” applyLabel=“false”>
<server>http://tfs1.TestProject.com:8080</server>
<domain>TestProject.com</domain>
<project>$/TestProject projects/Analysis and Design\Concierge\Prototype</project>
<workingDirectory>E:\dailybuild\TestProject</workingDirectory>
<cleanCopy>true</cleanCopy>
</sourcecontrol>
View Code
对于使用svn源码管理器,可以使用以下配置:
<artifactDirectory>d:\svn\Log\MyProject</artifactDirectory>
<sourcecontrol type=“svn”>
<executable>C:\Program Files\TortoiseSVN\bin\svn.exe</executable>
<username>UserName</username>
<password>******</password>
<autoGetSource>true</autoGetSource>
<trunkUrl>http://svnserver/trunk/MyProject</trunkUrl>
<workingDirectory>d:\svn\trunk\MyProject</workingDirectory>
</sourcecontrol>
View Code
2.实现自动Build,向Project节点下增加tasks节点,如下
<tasks>
<msbuild>
<executable>C:\Windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe</executable>
<buildArgs>/p:OutputPath=D:\BuildOutput</buildArgs>
<workingDirectory>D:\svn\branch\project\</workingDirectory>
<projectFile>mysolution.sln</projectFile>
<targets>Build</targets>
<timeout>9000</timeout>
</msbuild>
</tasks>
View Code
3.自动执行UnitTest,并生成单元测试报告
如果要生成单元测试报告和单元测试覆盖率,这里需要多下写功夫去配置。首先在创建Runsettings文件,如下:
CodeCoverage.runsettings的配置内容如下:
<RunSettings>
<RunConfiguration>
<ResultsDirectory>d:\svn\log\TestResults</ResultsDirectory>
<!-- \[x86\] | x64
- You can also change it from menu Test, Test Settings, Default Processor Architecture \-->
<TargetPlatform\>x86</TargetPlatform\>
<!-- Framework35 | \[Framework40\] | Framework45 \-->
<TargetFrameworkVersion\>Framework40</TargetFrameworkVersion\>
</RunConfiguration>
<DataCollectionRunSettings>
<DataCollectors>
<DataCollector friendlyName=“Code Coverage” uri=“datacollector://Microsoft/CodeCoverage/2.0” assemblyQualifiedName=“Microsoft.VisualStudio.Coverage.DynamicCoverageDataCollector, Microsoft.VisualStudio.TraceCollector, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a”>
<Configuration>
<CodeCoverage>
<ModulePaths>
<Include>
<ModulePath>.*\.dll$</ModulePath>
<ModulePath>.*\.exe$</ModulePath>
</Include>
<Exclude>
<ModulePath>.*CPPUnitTestFramework.*</ModulePath>
</Exclude>
</ModulePaths>
<!-- Match fully qualified names of functions: \-->
<!-- (Use "\\." to delimit namespaces in C# or Visual Basic, "::" in C++.) \-->
<Functions\>
<Exclude\>
<Function\>^Fabrikam\\.UnitTest\\..\*</Function\>
<Function\>^std::.\*</Function\>
<Function\>^ATL::.\*</Function\>
<Function\>.\*::\_\_GetTestMethodInfo.\*</Function\>
<Function\>^Microsoft::VisualStudio::CppCodeCoverageFramework::.\*</Function\>
<Function\>^Microsoft::VisualStudio::CppUnitTestFramework::.\*</Function\>
</Exclude\>
</Functions\>
<!-- Match attributes on any code element: \-->
<Attributes\>
<Exclude\>
<!-- Don’t forget "Attribute" at the end of the name \-->
<Attribute\>^System.Diagnostics.DebuggerHiddenAttribute$</Attribute\>
<Attribute\>^System.Diagnostics.DebuggerNonUserCodeAttribute$</Attribute\>
<Attribute\>^System.Runtime.CompilerServices.CompilerGeneratedAttribute$</Attribute\>
<Attribute\>^System.CodeDom.Compiler.GeneratedCodeAttribute$</Attribute\>
<Attribute\>^System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute$</Attribute\>
</Exclude\>
</Attributes\>
<!-- Match the path of the source files in which each method is defined: \-->
<Sources\>
<Exclude\>
<Source\>.\*\\\\atlmfc\\\\.\*</Source\>
<Source\>.\*\\\\vctools\\\\.\*</Source\>
<Source\>.\*\\\\public\\\\sdk\\\\.\*</Source\>
<Source\>.\*\\\\microsoft sdks\\\\.\*</Source\>
<Source\>.\*\\\\vc\\\\include\\\\.\*</Source\>
</Exclude\>
</Sources\>
<!-- Match the company name property in the assembly: \-->
<CompanyNames\>
<Exclude\>
<CompanyName\>.\*microsoft.\*</CompanyName\>
</Exclude\>
</CompanyNames\>
<!-- Match the public key token of a signed assembly: \-->
<PublicKeyTokens\>
<!-- Exclude Visual Studio extensions: \-->
<Exclude\>
<PublicKeyToken\>^B77A5C561934E089$</PublicKeyToken\>
<PublicKeyToken\>^B03F5F7F11D50A3A$</PublicKeyToken\>
<PublicKeyToken\>^31BF3856AD364E35$</PublicKeyToken\>
<PublicKeyToken\>^89845DCD8080CC91$</PublicKeyToken\>
<PublicKeyToken\>^71E9BCE111E9429C$</PublicKeyToken\>
<PublicKeyToken\>^8F50407C4E9E73B6$</PublicKeyToken\>
<PublicKeyToken\>^E361AF139669C375$</PublicKeyToken\>
</Exclude\>
</PublicKeyTokens\>
<!-- We recommend you do not change the following values: \-->
<UseVerifiableInstrumentation\>True</UseVerifiableInstrumentation\>
<AllowLowIntegrityProcesses\>True</AllowLowIntegrityProcesses\>
<CollectFromChildProcesses\>True</CollectFromChildProcesses\>
<CollectAspDotNet\>False</CollectAspDotNet\>
</CodeCoverage\>
</Configuration\>
</DataCollector\>
</DataCollectors\>
</DataCollectionRunSettings>
</RunSettings>
View Code
里面最重要的信息室配置了单元测试结果存放路径:
接着来配置ccnet.config,以执行单元测试
<exec>
<executable>D:\svn\tool\delfile.bat</executable>
<buildArgs>D:\svn\Log\TestResultsReal\mstest-results.trx</buildArgs>
<buildTimeoutSeconds>300</buildTimeoutSeconds>
<successExitCodes>-1,0</successExitCodes>
</exec>
<exec>
<executable>D:\svn\tool\delfile.bat</executable>
<buildArgs>D:\svn\Log\TestResultsReal\mstest-coverage.xml</buildArgs>
<buildTimeoutSeconds>300</buildTimeoutSeconds>
<successExitCodes>-1,0</successExitCodes>
</exec>
<exec>
<executable>C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE\CommonExtensions\Microsoft\TestWindow\VSTest.Console.exe</executable>
<baseDirectory>D:\svn\UnitTest.dll所在的文件目录</baseDirectory>
<buildArgs>UnitTest.dll /Enablecodecoverage /Settings:D:\svn\tool\CodeCoverage.runsettings /logger:trx</buildArgs>
<buildTimeoutSeconds>300</buildTimeoutSeconds>
</exec>
<exec>
<executable>
<exec>
<executable>D:\svn\tool\delfile.bat</executable>
<buildArgs>D:\svn\Log\TestResultsReal\mstest-results.trx</buildArgs>
<buildTimeoutSeconds>300</buildTimeoutSeconds>
<successExitCodes>-1,0</successExitCodes>
</exec>
<exec>
<executable>D:\svn\tool\delfile.bat</executable>
<buildArgs>D:\svn\Log\TestResultsReal\mstest-coverage.xml</buildArgs>
<buildTimeoutSeconds>300</buildTimeoutSeconds>
<successExitCodes>-1,0</successExitCodes>
</exec>
<exec>
<executable>C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE\CommonExtensions\Microsoft\TestWindow\VSTest.Console.exe</executable>
<baseDirectory>D:\svn\UnitTest.dll所在的文件目录</baseDirectory>
<buildArgs>UnitTest.dll /Enablecodecoverage /Settings:D:\svn\tool\CodeCoverage.runsettings /logger:trx</buildArgs>
<buildTimeoutSeconds>300</buildTimeoutSeconds>
</exec>
<exec>
<executable>D:\svn\tool\coverage\Auto.Dealer.UnitTest.Tool.exe</executable>
<buildArgs>D:\svn\log\TestResults\ D:\svn\log\TestResultsReal\mstest-results.trx D:\svn\log\TestResultsReal\mstest-coverage.xml</buildArgs>
<buildTimeoutSeconds>300</buildTimeoutSeconds>
<successExitCodes>-1,0</successExitCodes>
</exec>
<!--合并xml文件\-->
<merge\>
<files\>
<file\>D:\\svn\\Log\\TestResultsReal\\mstest-results.trx</file\>
<!--这个文件是用于合并UnitTest result\-->
</files\>
</merge\>
<merge\>
<files\>
<file\>D:\\svn\\Log\\TestResultsReal\\mstest-coverage.xml</file\>
<!--这个文件是用于合并UnitTest result\-->
</files\>
</merge\>D:\\svn\\tool\\coverage\\Auto.Dealer.UnitTest.Tool.exe</executable\>
<buildArgs>D:\svn\log\TestResults\ D:\svn\log\TestResultsReal\mstest-results.trx D:\svn\log\TestResultsReal\mstest-coverage.xml</buildArgs>
<buildTimeoutSeconds>300</buildTimeoutSeconds>
<successExitCodes>-1,0</successExitCodes>
</exec>
<merge>
<files>
<file>D:\svn\Log\TestResultsReal\mstest-results.trx</file>
</files>
</merge>
<merge>
<files>
<file>D:\svn\Log\TestResultsReal\mstest-coverage.xml</file>
</files>
</merge>
View Code
这里,想必大家已经注意到有两处删除操作,因为持续集成式一个不断重复的过程,如果不删除原来的测试结果就会发生错误。另外,用到的一个工具(这个工具里的代码很简单,一并提供出来如下),用来将trx文件转化为xml文件,即单元测试覆盖率结果。最后将它们一起合并到CruiseControl的执行日志里。
生成单元测试覆盖率代码如下:
class Program
{ static void Main(string\[\] args)
{ string dirName = args\[0\]; string trxUutPutFileName = args\[1\]; string coverageoutPutFileName = args\[2\]; if (Directory.Exists(dirName))
{
DirectoryInfo dirc \= new DirectoryInfo(dirName); foreach (FileInfo file in dirc.GetFiles("\*.trx"))
{
file.CopyTo(trxUutPutFileName, true); break;
} foreach (FileInfo file in dirc.GetFiles("\*.coverage", SearchOption.AllDirectories))
{
ConvertToXML(file.FullName, coverageoutPutFileName); break;
}
dirc.Delete(true);
} else {
Console.WriteLine("没找到目录:"+dirName);
}
} public static void ConvertToXML(string coverageFile, string outputFile)
{ using( CoverageInfo coverageInfo = CoverageInfo.CreateFromFile(coverageFile))
{ using (CoverageDS ds = coverageInfo.BuildDataSet())
{
ds.ExportXml(outputFile);
}
}
}
}
View Code
4.自动部署
<buildpublisher>
<sourceDir>d:\svn\_PublishedWebsites\MyWeb</sourceDir>
<publishDir>\\IP地址\website\</publishDir>
<useLabelSubDirectory>false</useLabelSubDirectory>
</buildpublisher>
View Code
5. 触发自动化(回归)测试
如果有自动化测试框架,则可以考虑部署完毕后自动触发执行自动化测试,由于自动化测试框架可能会有很大差异,这里就不在给出配置,总的来说,使用
**6. 邮件提醒功能
**
无论持续集成执行成功,还是失败,都可以配置相应的邮件接收人员。邮件配置要放到
<publishers>
<email mailport=“25” includeDetails=“TRUE” mailhostUsername=“my@sina.com“ mailhostPassword=“******“ useSSL=“FALSE”>
<from>my@sina.com</from>
<mailhost>smtp.sina.net</mailhost>
<users>
<user name=“张三” group=“developers” address=“123@sina.com“ />
<user name=“李四” group=“developers” address=“456@sina.com“ />
<user name=“王五” group=“developers” address=“789@sina.com“ />
</users>
<groups>
<group name=“developers”>
<notifications>
<notificationType>Failed</notificationType>
<notificationType>Fixed</notificationType>
</notifications>
</group>
<group name=“buildmaster”>
<notifications>
<notificationType>Always</notificationType>
</notifications>
</group>
</groups>
<converters>
<regexConverter find=“$” replace=“@sina.com” />
</converters>
<modifierNotificationTypes>
<NotificationType>Failed</NotificationType>
<NotificationType>Fixed</NotificationType>
</modifierNotificationTypes>
<subjectSettings>
<subject buildResult=“StillBroken” value=“Build is still broken for {CCNetProject}” />
</subjectSettings>
</email>
</publishers>
View Code
**7. 使用CCTray监控持续集成
**
打开http://CruiseControl所在机的IP/ccnet/,可以看到如下连接,下载并安装。就可以监控制定的项目了。
8.应用plugin显示集成结果
<buildPlugins>
<buildReportBuildPlugin>
<xslFileNames>
<xslFile>xsl\header.xsl</xslFile>
<xslFile>xsl\modifications.xsl</xslFile>
<xslFile>xsl\unittests.xsl</xslFile>
<xslFile>xsl\MsTestSummary2008.xsl</xslFile>
<xslFile>xsl\compile-msbuild.xsl</xslFile>
<xslFile>xsl\SimianSummary.xsl</xslFile>
<xslFile>xsl\MsTestSummary2010.xsl</xslFile>
<xslFile>xsl\MsTestSummary.xsl</xslFile>
<xslFile>xsl\MsTestReport2010.xsl</xslFile>
<xslFile>xsl\MsTestCover2010.xsl</xslFile>
</xslFileNames>
</buildReportBuildPlugin>
</buildPlugins>
View Code
这样,重启ccnet站点后,我们的测试结果也会被格式化地显示出来。
9.设置集成策略
以每日构建为例,需要在
<scheduleTrigger time=“23:30” buildCondition=“ForceBuild” name=“Scheduled”>
<weekDays>
<weekDay>Monday</weekDay>
<weekDay>Tuesday</weekDay>
<weekDay>Wednesday</weekDay>
<weekDay>Thursday</weekDay>
<weekDay>Friday</weekDay>
</weekDays>
</scheduleTrigger>
View Code
至此,我们的持续集成也基本上可以告一段落了。可以使用它来进行一些自动化的工作了。