【Hello CC.NET】巧用模板简化配置
从 《【Hello CC.NET】CC.NET 实现自动化集成》 到 《【Hello CC.NET】自动化发布时 Web.config 文件维护》 ,大神在评论里提到的方案还没有一一去研究对比,调研的 deadline 已经逼近,CC.NET 的落地提上日程。
经过两周的努力,终于成功部署了两个项目,由于两个项目的人员配置/开发流程/项目架构不一样,部署的方案也有些差异。过程中碰到这样那样的坑,都是用最快想到的笨方法填坑,然后继续往下走。落地的姿势并不完美,但总算有了一些看得到的成果。
落地面临的第一个问题是,ccnet.config 配置比较繁复:太多的 hardcode;相同的 xml 节点内容比较多,尤其在配置多个项目的时候,策略变更时,需要改动的地方比较多。本文将讲述 ccnet.config 模板化的思路。
目标
- 提取全局性的配置,避免到处 Hardcode
- 简化各个节点的配置,便于维护
1.全局变量
<cc:define CIWebSite="http://vm-caihaihua/CCNET/"/> <cc:define CIDirectory="C:CI"/> <cc:define MsBuildPath="C:WindowsMicrosoft.NETFrameworkv4.0.30319MSBuild.exe"/> <cc:define MsTestPath="C:Program Files (x86)Microsoft Visual Studio 10.0Common7IDEMSTest.exe"/> <cc:define SVNPath="C:Program Files (x86)VisualSVN Serverinsvn.exe"/> <cc:define FCReplacerPath="C:CIToolsFCReplacer.exe"/> <cc:define DFCopierPath="C:CIToolsDFCopier.exe"/> <cc:define name="MsBuildXmlLogger"> <logger>ThoughtWorks.CruiseControl.MsBuild.XmlLogger,C:Program Files (x86)CruiseControl.NETserverThoughtWorks.CruiseControl.MsBuild.dll</logger> </cc:define>
变量名称 | 描述 | 值 |
CIWebSite | CC.NET配套网站的地址 | http://vm-caihaihua/CCNET/ |
CIDirectory | 工作目录 | C:CI |
MsBuildPath | MsBuild 程序的地址 | C:WindowsMicrosoft.NETFrameworkv4.0.30319MSBuild.exe |
MsTestPath | MsTest 程序的地址 | C:Program Files (x86)Microsoft Visual Studio 10.0Common7IDEMSTest.exe |
SVNPath | Svn 程序的地址 | C:Program Files (x86)VisualSVN Serverinsvn.exe |
FCReplacerPath | FCReplacer 程序的地址 | C:CIToolsFCReplacer.exe |
DFCopierPath | DFCopier 程序的地址 | C:CIToolsDFCopier.exe |
MsBuildXmlLogger | 日志 Logger 的配置 | <logger>ThoughtWorks.CruiseControl.MsBuild.XmlLogger,C:Program Files (x86)CruiseControl.NETserverThoughtWorks.CruiseControl.MsBuild.dll</logger> |
2.默认值
<!-- 默认配置 --> <cc:define QueuePriority="1"/> <cc:define KeepLastXBuilds="300"/> <!-- Email 相关 --> <cc:define EmailDomain="company.com"/> <cc:define EmailSendHost="smtp.qq.com"/> <cc:define EmailSendPort="25"/> <cc:define EmailUseSSL="FALSE"/> <cc:define EmailSendUser="ci"/> <cc:define EmailSendPassword="********"/> <cc:define CIEmailUser="ci"/> <cc:define PMEmailUser="pm_1"/> <cc:define name="EmailTesterList"> <user group="tester" name="tester_1" address="tester_1@$(EmailDomain)"/> <user group="tester" name="tester_2" address="tester_2@$(EmailDomain)"/> </cc:define> <!-- Trigger 相关 --> <cc:define ModificationTriggerIntervalSeconds="300"/> <cc:define ModificationDelaySeconds="60"/> <cc:define ProjectTriggerIntervalSeconds="30"/> <cc:define ProjectTriggerStatus="Success"/> <cc:define DailyBuildTriggerTime="18:00"/> <!-- SVN 相关 --> <cc:define SVNUsername="ci"/> <cc:define SVNPassword="********"/> <cc:define SvnCleanCopy="FALSE"/> <cc:define name="SourceControl"> </cc:define> <!-- Task 相关 --> <cc:define TestTimeout="1800"/> <cc:define CopyTimeout="60"/> <cc:define ReplaceTimeout="60"/> <cc:define BuildConfiguration="DEBUG"/> <cc:define PublishConfiguration="RELEASE"/>
变量名称 | 描述 | 值 |
QueuePriority | 项目在队列中的优先级 | 1 |
KeepLastXBuilds | 保留最后的 X 个构建结果 | 300 |
EmailDomain | 公司邮箱域名 | company.com |
EmailSendHost | 邮箱服务器地址 | smtp.live.com |
EmailSendPort | 邮箱服务器端口 | 25 |
EmailUseSSL | 是否启用 SSL | TRUE |
EmailSendUser | 发送邮箱的帐号 | ci |
EmailSendPassword | 发送邮箱账户的密码 | ****** |
CIEmailUser | CI 维护人员的邮箱账户 | ci |
PMEmailUser | 部门经理的邮箱账户 | pm_1 |
EmailTesterList | 测试人员邮箱列表 |
<user group="tester" name="tester_1" address="tester_1@$(EmailDomain)"/> <user group="tester" name="tester_2" address="tester_2@$(EmailDomain)"/> |
ModificationTriggerIntervalSeconds | 源代码监测的频率(秒) | 300 |
ModificationDelaySeconds | 源代码变更触发构建任务的延迟时间(秒) | 60 |
ProjectTriggerIntervalSeconds | 依赖项目的状态监测频率(秒) | 30 |
ProjectTriggerStatus | 依赖项目的设定状态 | SUCCESS |
DailyBuildTriggerTime | 每日构建的时间点 | 18:00 |
SVNUsername | SVN 帐号 | ci |
SVNPassword | SVN 帐号的密码 | ****** |
SvnCleanCopy | 下载源代码前是否先删除本地源代码 | FALSE |
SourceControl | 源代码管理的配置 | |
TestTimeout | 运行测试的超时时间(秒) | 1800 |
CopyTimeout | 拷贝文件的超时时间(秒) | 60 |
ReplaceTimeout | 替换文件内容的超时时间(秒) | 60 |
BuildConfiguration | 编译的配置 | DEBUG |
PublishConfiguration | 发布网站的配置 | RELEASE |
3.通知模板
<cc:define name="EmailPublisher"> <email mailhost="$(EmailSendHost)" mailport="$(EmailSendPort)" mailhostUsername="$(EmailSendUser)@$(EmailDomain)" mailhostPassword="$(EmailSendPassword)" from="$(EmailSendUser)@$(EmailDomain)" useSSL="$(EmailUseSSL)" includeDetails="true"> <!--邮件标题配置--> <subjectPrefix>$(EmailSubject)</subjectPrefix> <subjectSettings> <!-- Success/Broken/StillBroken/Fixed/Exception--> <subject buildResult="Success" value="${CCNetProject} Build result"/> <subject buildResult="Fixed" value="${CCNetProject} Build result"/> <subject buildResult="Broken" value="${CCNetProject} Build result"/> <subject buildResult="StillBroken" value="${CCNetProject} Build result" /> <subject buildResult="Exception" value="${CCNetProject} Build result" /> </subjectSettings> <!--收件人配置--> <converters> <rcgexConverter find="$" replace="@$(EmailDomain)"/> </converters> <modifierNotificationTypes> <notificationType>Failed</notificationType> <notificationType>Fixed</notificationType> </modifierNotificationTypes> <users> <user group="pm" name="$(PMEmailUser)" address="$(PMEmailUser)@$(EmailDomain)"/> <user group="ci" name="$(CIEmailUser)" address="$(CIEmailUser)@$(EmailDomain)"/> <user group="tl" name="$(TeamLeader)" address="$(TeamLeader)@$(EmailDomain)"/> <cc:EmailTesterList/> </users> <groups> <group name="pm"> <notifications> <!--Always/Success/Change/Fixed/Failed/Exception --> <notificationType>Change</notificationType> </notifications> </group> <group name="ci"> <notifications> <notificationType>Exception</notificationType> </notifications> </group> <group name="tl"> <notifications> <notificationType>Success</notificationType> <notificationType>Fixed</notificationType> <notificationType>Failed</notificationType> </notifications> </group> <group name="developer"> <notifications> <notificationType>Success</notificationType> <notificationType>Fixed</notificationType> <notificationType>Failed</notificationType> </notifications> </group> <group name="tester"> <notifications> <notificationType>Fixed</notificationType> </notifications> </group> </groups> </email> </cc:define>
模板变量:
变量名称 | 描述 | 默认值 | 是否必填 |
EmailDomain | 公司邮箱域名 | company.com | N |
EmailSendHost | 邮件服务器的地址 | smtp.live.com | N |
EmailSendPort | 邮件服务器的端口 | 25 | N |
EmailUseSSL | 是否使用 SSL(取决于邮件服务器的配置) | TRUE | N |
EmailSendUser | 邮件通知的发送帐号 | CI | N |
EmailSendPassword | 邮件通知的发送帐号的密码 | ****** | N |
PMEmailUser | 部门经理的邮箱帐号 | pm_1 | N |
CIEmailUser | CI 环境维护人员的邮箱帐号 | ci | N |
TeamLeader | 项目组负责人的邮箱帐号,各个项目组配置不同 | Y | |
EmailSubject | 邮件通知的前缀 | Y |
例子:
<publishers> <!--邮件通知--> <cc:EmailPublisher/> </publishers>
4.触发器模板
(1)源代码检测:每隔一段时间检查源代码,如果有变更,则触发构建过程
<!-- Triggers:ModificationTrigger/DailyBuildTrigger/ProjectTrigger--> <cc:define name="ModificationTrigger"> <!--源码修改触发--> <intervalTrigger seconds="$(ModificationTriggerIntervalSeconds)" buildCondition="IfModificationExists" /> </cc:define>
模板变量:
变量名称 | 描述 | 默认值 | 是否必填 |
ModificationTriggerIntervalSeconds | 源码监测频率(秒) | 600 | N |
例子:
<triggers> <cc:ModificationTrigger/> </triggers>
(2)每日构建:每天到设定的时间点触发构建过程
<cc:define name="DailyBuildTrigger"> <!--每日构建--> <scheduleTrigger time="$(DailyBuildTriggerTime)" buildCondition="ForceBuild"> <weekDays> <!--<weekDay>Sunday</weekDay>--> <weekDay>Monday</weekDay> <weekDay>Tuesday</weekDay> <weekDay>Wednesday</weekDay> <weekDay>Thursday</weekDay> <weekDay>Friday</weekDay> <!--<weekDay>Saturday</weekDay>--> </weekDays> </scheduleTrigger> </cc:define>
模板变量:
变量名称 | 描述 | 默认值 | 是否必填 |
DailyBuildTriggerTime | 每日构建的时间点 | 18:00 | N |
例子:
<triggers> <cc:DailyBuildTrigger/> </triggers>
(3)项目依赖:每隔一段时间检查指定项目的构建结果,如果等于指定值,则触发构建过程
<cc:define name="ProjectTrigger"> <projectTrigger serverUri="tcp://vm-caihaihua:21234/CruiseManager.rem" project="$(ListenToProject)"> <triggerStatus>$(ProjectTriggerStatus)</triggerStatus> <innerTrigger type="intervalTrigger" seconds="$(ProjectTriggerIntervalSeconds)" buildCondition="ForceBuild" /> <!--<triggerFirstTime>True</triggerFirstTime>--> </projectTrigger> </cc:define>
模板变量:
变量名称 | 描述 | 默认值 | 是否必填 |
ProjectTriggerIntervalSeconds | 监测依赖项目的频率(秒) | 30 | N |
ProjectTriggerStatus | 设定的构建结果 | SUCCESS | N |
ListenToProject | 依赖的项目名称 | Y |
例子:
<triggers> <cc:ProjectTrigger ListenToProject="TestProject-Servcies"/> </triggers>
5.源代码管理模板
<!-- SourceControl: SVNSourceControl --> <cc:define name="SVNSourceControl"> <sourcecontrol type="svn"> <trunkUrl>$(SVNTrunkURL)</trunkUrl> <executable>$(SVNPath)</executable> <workingDirectory>$(CIDirectory)$(WorkingDirectory)</workingDirectory> <username>$(SVNUsername)</username> <password>$(SVNPassword)</password> <revert>True</revert> <cleanCopy>$(SvnCleanCopy)</cleanCopy> <cleanUp>True</cleanUp> <deleteObstructions>True</deleteObstructions> </sourcecontrol> </cc:define>
模板变量:
变量名称 | 描述 | 默认值 | 是否必填 |
SVNUsername | Svn 账户,需要项目的 checkout 权限 |
ci | N |
SVNPassword | Svn 账户的密码 |
****** | N |
SvnCleanCopy | 下载源代码前是否先删除本地源代码 | FALSE | N |
SVNTrunkURL | 项目的 Svn 路径 |
Y |
例子:
<project> <cc:SVNSourceControl SVNTrunkURL="https://vm-caihaihua/svn/testproject/services/trunk/" SvnCleanCopy="False"/> </project>
6.任务模板
(1)文件内容替换
<cc:define name="Replace"> <exec> <executable>$(FCReplacerPath)</executable> <buildArgs> <cc:Files/> <cc:Rules/> </buildArgs> <buildTimeoutSeconds>$(ReplaceTimeout)</buildTimeoutSeconds> <successExitCodes>0</successExitCodes> </exec> </cc:define>
模板变量:
变量名称 | 描述 | 默认值 | 是否必填 |
ReplaceTimeout | 替换的超时时间(秒) | 60 | N |
Files | 需要替换的文件列表 | Y | |
Rules | 替换的规则 |
Y |
例子:
<tasks> <!-- Config --> <cc:Replace> <cc:define name="Files"> /file=$(CIDirectory)$(WorkingDirectory)WcfServiceWeb.config /file=$(CIDirectory)$(WorkingDirectory)ManagerTestApp.config /file=$(CIDirectory)$(WorkingDirectory)WcfServiceTestApp.config </cc:define> <cc:define name="Rules"> /from=Catalog=(?![^;s']*_log[;s']+)(=?[^;s']*)/to=Catalog=testproject_trunk /from=Source=(=?.*?)(=?[;s']+)/to=Source=vm-caihaihua$2 /from=http://(=?[^/]+)/(=?w+).svc/to=http://vm-caihaihua/testproject_trunk_ci/WcfService/$2.svc /from=http://(=?.*?)/(?!SSO)(=?[^/]+)/(=?w+).svc/to=http://vm-caihaihua/testproject_trunk_ci/WcfService/$3.svc /from=http://(=?.*?)/SSO/(=?w+).svc/to=http://vm-caihaihua/testproject_trunk_ci/SSO/$2.svc </cc:define> </cc:Replace> </tasks>
(2)项目编译
<cc:define name="Build"> <msbuild> <executable>$(MsBuildPath)</executable> <buildArgs>/t:build /p:configuration=$(BuildConfiguration)</buildArgs> <cc:MsBuildXmlLogger/> <workingDirectory>$(CIDirectory)$(WorkingDirectory)</workingDirectory> <projectFile>$(SolutionPath)</projectFile> </msbuild> </cc:define>
模板变量:
变量名称 | 描述 | 默认值 | 是否必填 |
BuildConfiguration | 编译的配置 | DEBUG | N |
SolutionPath | 项目的相对路径 | Y |
例子:
<tasks> <!-- Build --> <cc:Build SolutionPath="TestProjectService.sln"/> </tasks>
(3)网站发布
<cc:define name="Publish"> <msbuild> <executable>$(MsBuildPath)</executable> <workingDirectory>$(CIDirectory)$(WorkingDirectory)</workingDirectory> <projectFile>$(SolutionPath)</projectFile> <buildArgs> /t:ResolveReferences;Compile /t:_CopyWebApplication /p:Configuration=$(PublishConfiguration) /p:WebProjectOutputDir=$(CIDirectory)$(To) /p:OutputPath=$(CIDirectory)$(To)in </buildArgs> </msbuild> </cc:define>
模板变量:
变量名称 | 描述 | 默认值 | 是否必填 |
PublishConfiguration | 编译的配置 | RELACE | N |
SolutionPath | 项目的相对路径 | Y | |
To | 目标文件夹的相对路径 | Y |
例子:
<tasks> <!-- Publish --> <cc:Publish SolutionPath="WcfServiceWcfService.csproj" To="$(PublishDirectory)WcfService" /> </tasks>
(4)文件拷贝
<cc:define name="Copy"> <exec> <executable>$(DFCopierPath)</executable> <buildArgs> /from=$(CIDirectory)$(From) /to=$(CIDirectory)$(To) </buildArgs> <buildTimeoutSeconds>$(CopyTimeout)</buildTimeoutSeconds> <successExitCodes>0</successExitCodes> </exec> </cc:define>
模板变量:
变量名称 | 描述 | 默认值 | 是否必填 |
CopyTimeout | 拷贝的超时时间(秒) | 60 | N |
From | 需要拷贝的文件夹的相对路径 | Y | |
To | 目标文件夹的相对路径 | Y |
例子:
<tasks> <!-- Copy--> <cc:Copy From="$(WorkingDirectory)WcfService To="$(PublishDirectory)WcfService"/> </tasks>
(5)自动化测试
<cc:define name="RunTest"> <exec> <executable>$(MSTestPath)</executable> <baseDirectory>$(CIDirectory)$(WorkingDirectory)</baseDirectory> <buildArgs>/testcontainer:$(TestContainerPath)</buildArgs> <buildTimeoutSeconds>$(TestTimeout)</buildTimeoutSeconds> </exec> </cc:define>
模板变量:
变量名称 | 描述 | 默认值 | 是否必填 |
TestTimeout | 测试的超时时间(秒) | 1800 | N |
TestContainerPath | 测试列表的相对路径 | Y |
例子:
<tasks> <!--Run Test--> <cc:RunTest TestContainerPath="ManagerTestinDebugManagerTest.dll"/> <cc:RunTest TestContainerPath="WcfServiceTestinDebugWcfServiceTest.dll"/> </tasks>
7.项目模板
<cc:define name="Project"> <project name="$(ProjectName)" queue="$(Queue)" queuePriority="$(QueuePriority)"> <webURL>$(CIWebSite)server/local/project/$(ProjectName)/ViewProjectReport.aspx</webURL> <!--标签--> <labeller type="dateLabeller"/> <artifactDirectory>$(CIDirectory)$(ArtifactDirectory)</artifactDirectory> <!--项目的目录--> <workingDirectory >$(CIDirectory)$(WorkingDirectory)</workingDirectory> <!--自动运行时间间隔--> <triggers> <cc:TriggerList/> </triggers> <!--对源码修改延迟处理时间间隔--> <modificationDelaySeconds>$(ModificationDelaySeconds)</modificationDelaySeconds> <maxSourceControlRetries>5</maxSourceControlRetries> <!--源代码管理(SVN)--> <cc:SourceControl/> <state type="state" directory="$(CIDirectory)$(StateDirectory)"/> <publishers> <modificationHistory onlyLogWhenChangesFound="true"/> <statistics/> <xmllogger/> <artifactcleanup cleanUpMethod="KeepLastXBuilds" cleanUpValue="$(KeepLastXBuilds)" /> <!--邮件通知--> <cc:EmailPublisher/> </publishers> <tasks> <cc:TaskList/> </tasks> </project> </cc:define>
模板变量:
变量名称 | 描述 | 默认值 | 是否必填 |
QueuePriority | 项目在队列里的优先级 | 1 | N |
KeepLastXBuilds | 保存最后的 X 个构建结果 | 300 | N |
ModificationDelaySeconds | 源代码变更触发构建任务的延迟时间 | 60 | N |
SourceControl | 源代码配置 | N | |
Queue | 项目从属的队列 | Y | |
ProjectName | 项目名称 | Y | |
StateDirectory | 构建日志的保存目录 | Y | |
WorkingDirectory | 项目的工作目录,即源代码的保存路径 | Y | |
ArtifactDirectory | 构建结果的存放目录 | Y | |
TriggerList | 触发器配置列表 | Y | |
TaskList | 任务列表 | Y |
例子:
TestProject 包括 Wcf Service 项目和 AdminWeb 项目
(1)Wcf Service
a. 自动化部署
-
- 自动获取源代码
- 源代码监测及每日构建
- 修改配置文件中的数据库连接字符串及服务引用地址
- Debug 编译整个解决方案
- Release 发布 Service 到 CI 环境
b. 自动化测试
-
- 与a使用同一个工作目录(即源代码)
- 监测自动化部署过程,如果成功则触发自动化测试
- 运行 ManagerTest 和 ServiceTest
(2) AdminWeb
自动化部署
- 自动获取源代码
- 源代码监测及每日构建
- 修改配置文件中的数据库连接字符串及服务引用地址
- Debug 编译整个解决方案
- Release 发布 AdminWeb 到 CI 环境
<!-- TestProject: Service 项目 --> <queue name="TestProject" duplicates="ApplyForceBuildsReAdd" /> <cc:Project ProjectName="TestProject-Servcies" Queue="TesProjectt" WorkingDirectory="TestProjectSourceCodeservice_trunk" ArtifactDirectory="TestProjectArtifactservice" StateDirectory="TestProjectState" PublishDirectory="TestProjectPublish" EmailSubject="[CI-TestProjectService]" ModificationDelaySeconds="30" TeamLeader="caihaihua"> <cc:define name="SourceControl"> <cc:SVNSourceControl SVNTrunkURL="https://vm-caihaihua/svn/testproject/services/trunk/" SvnCleanCopy="False"/> </cc:define> <cc:define name="TriggerList"> <cc:ModificationTrigger/> <cc:DailyBuildTrigger/> </cc:define> <cc:define name="TaskList"> <!-- Config --> <cc:Replace> <cc:define name="Files"> /file=$(CIDirectory)$(WorkingDirectory)WcfServiceWeb.config /file=$(CIDirectory)$(WorkingDirectory)ManagerTestApp.config /file=$(CIDirectory)$(WorkingDirectory)WcfServiceTestApp.config </cc:define> <cc:define name="Rules"> /from=Catalog=(?![^;s']*_log[;s']+)(=?[^;s']*)/to=Catalog=service_trunk_ci /from=Source=(=?.*?)(=?[;s']+)/to=Source=localhost$2 /from=http://(=?[^/]+)/(=?[^/]+).svc/to=http://vm-caihaihua/testproject_trunk_ci/WcfService/$2.svc /from=http://(=?.*?)/(?!SSO)(=?[^/]+)/(=?[^/]+).svc/to=http://vm-caihaihua/testproject_trunk_ci/WcfService/$3.svc /from=http://(=?.*?)/SSO/(=?[^/]+).svc/to=http://vm-caihaihua/testproject_trunk_ci/SSO/$2.svc </cc:define> </cc:Replace> <!-- Build --> <cc:Build SolutionPath="TestProjectService.sln"/> <!-- Publish --> <cc:Copy From="$(WorkingDirectory)WcfService" To="$(PublishDirectory)WcfService"/> </cc:define> </cc:Project> <cc:Project ProjectName="TestProjectServcies-Test" Queue="TestProject" QueuePriority="2" WorkingDirectory="TestProjectSourceCodeservice_trunk" ArtifactDirectory="TestProjectArtifact est" StateDirectory="TestProjectState" EmailSubject="[CI-TestProjectServcie-Test]" ModificationDelaySeconds="30" TeamLeader="caihaihua"> <cc:define name="TriggerList"> <cc:ProjectTrigger ListenToProject="TestProject-Servcies"/> </cc:define> <cc:define name="TaskList"> <!--Run Test--> <cc:RunTest TestContainerPath="ManagerTestinDebugManagerTest.dll"/> <cc:RunTest TestContainerPath="WcfServiceTestinDebugWcfServiceTest.dll"/> </cc:define> </cc:Project> <!-- TestProject: AdminWeb 项目 --> <cc:Project ProjectName="TestProject-AdminWeb" Queue="TestProject" WorkingDirectory="TestProjectSourceCodeadmin_trunk" ArtifactDirectory="TestProjectArtifactadmin" StateDirectory="TestProjectState" PublishDirectory="TestProjectPublish" EmailSubject="[CI-TestProject-AdminWeb]" TeamLeader="caihaihua"> <cc:define name="SourceControl"> <cc:SVNSourceControl SVNTrunkURL="https://vm-caihaihua/svn/testproject/adminweb/trunk/"/> </cc:define> <cc:define name="TriggerList"> <cc:ModificationTrigger/> <cc:DailyBuildTrigger/> </cc:define> <cc:define name="TaskList"> <!-- Config --> <cc:Replace> <cc:define name="Files"> /file=$(CIDirectory)$(WorkingDirectory)AdminWebWeb.config </cc:define> <cc:define name="Rules"> /from=http://(=?[^/]+)/(=?[^/]+).svc/to=http://vm-caihaihua/testproject_trunk_ci/WcfService/$2.svc /from=http://(=?.*?)/(?!SSO)(=?[^/]+)/(=?[^/]+).svc/to=http://vm-caihaihua/testproject_trunk_ci/WcfService/$3.svc /from=http://(=?.*?)/SSO/(=?[^/]+).svc/to=http://vm-caihaihua/testproject_trunk_ci/SSO/$2.svc </cc:define> </cc:Replace> <!-- Build --> <cc:Build SolutionPath="AdminWeb.sln"/> <!-- Publish --> <cc:Publish SolutionPath="AdminWebAdminWeb.csproj" To="$(PublishDirectory)AdminWeb" /> </cc:define> </cc:Project>
完整的 ccnet.config:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<cruisecontrol xmlns:cc="urn:ccnet.config.builder">
<!-- 全局配置 -->
<cc:define CIWebSite="http://vm-caihaihua/CCNET/"/>
<cc:define CIDirectory="C:CI"/>
<cc:define MsBuildPath="C:WindowsMicrosoft.NETFrameworkv4.0.30319MSBuild.exe"/>
<cc:define MsTestPath="C:Program Files (x86)Microsoft Visual Studio 10.0Common7IDEMSTest.exe"/>
<cc:define SVNPath="C:Program Files (x86)VisualSVN Serverinsvn.exe"/>
<cc:define FCReplacerPath="C:CIToolsFCReplacer.exe"/>
<cc:define DFCopierPath="C:CIToolsDFCopier.exe"/>
<cc:define name="MsBuildXmlLogger">
<logger>ThoughtWorks.CruiseControl.MsBuild.XmlLogger,C:Program Files (x86)CruiseControl.NETserverThoughtWorks.CruiseControl.MsBuild.dll</logger>
</cc:define>
<!-- Publishers:EmailPublisher-->
<cc:define name="EmailPublisher">
<email mailhost="$(EmailSendHost)" mailport="$(EmailSendPort)"
mailhostUsername="$(EmailSendUser)@$(EmailDomain)" mailhostPassword="$(EmailSendPassword)"
from="$(EmailSendUser)@$(EmailDomain)"
useSSL="$(EmailUseSSL)" includeDetails="true">
<!--邮件标题配置-->
<subjectPrefix>$(EmailSubject)</subjectPrefix>
<subjectSettings>
<!-- Success/Broken/StillBroken/Fixed/Exception-->
<subject buildResult="Success" value="${CCNetProject} ${CCNetBuildDate} Build result"/>
<subject buildResult="Fixed" value="${CCNetProject} ${CCNetBuildDate} Build result"/>
<subject buildResult="Broken" value="${CCNetProject} ${CCNetBuildDate} Build result"/>
<subject buildResult="StillBroken" value="${CCNetProject} ${CCNetBuildDate} Build result" />
<subject buildResult="Exception" value="${CCNetProject} ${CCNetBuildDate} Build result" />
</subjectSettings>lo
<!--收件人配置-->
<converters>
<regexConverter find="$" replace="@$(EmailDomain)"/>
</converters>
<modifierNotificationTypes>
<notificationType>Failed</notificationType>
<notificationType>Fixed</notificationType>
</modifierNotificationTypes>
<users>
<user group="pm" name="$(PMEmailUser)" address="$(PMEmailUser)@$(EmailDomain)"/>
<user group="ci" name="$(CIEmailUser)" address="$(CIEmailUser)@$(EmailDomain)"/>
<user group="tl" name="$(TeamLeader)" address="$(TeamLeader)@$(EmailDomain)"/>
<cc:EmailTesterList/>
</users>
<groups>
<group name="pm">
<notifications>
<!--Always/Success/Change/Fixed/Failed/Exception -->
<notificationType>Change</notificationType>
</notifications>
</group>
<group name="ci">
<notifications>
<notificationType>Exception</notificationType>
</notifications>
</group>
<group name="tl">
<notifications>
<notificationType>Success</notificationType>
<notificationType>Fixed</notificationType>
<notificationType>Failed</notificationType>
</notifications>
</group>
<group name="developer">
<notifications>
<notificationType>Success</notificationType>
<notificationType>Fixed</notificationType>
<notificationType>Failed</notificationType>
</notifications>
</group>
<group name="tester">
<notifications>
<notificationType>Fixed</notificationType>
</notifications>
</group>
</groups>
</email>
</cc:define>
<!-- Triggers:ModificationTrigger/DailyBuildTrigger/ProjectTrigger-->
<cc:define name="ModificationTrigger">
<!--源码修改触发-->
<intervalTrigger seconds="$(ModificationTriggerIntervalSeconds)" buildCondition="IfModificationExists" />
</cc:define>
<cc:define name="DailyBuildTrigger">
<!--每日构建-->
<scheduleTrigger time="$(DailyBuildTriggerTime)" buildCondition="ForceBuild">
<weekDays>
<!--<weekDay>Sunday</weekDay>-->
<weekDay>Monday</weekDay>
<weekDay>Tuesday</weekDay>
<weekDay>Wednesday</weekDay>
<weekDay>Thursday</weekDay>
<weekDay>Friday</weekDay>
<!--<weekDay>Saturday</weekDay>-->
</weekDays>
</scheduleTrigger>
</cc:define>
<cc:define name="ProjectTrigger">
<projectTrigger serverUri="tcp://Server12:21234/CruiseManager.rem" project="$(ListenToProject)">
<triggerStatus>$(ProjectTriggerStatus)</triggerStatus>
<innerTrigger type="intervalTrigger" seconds="$(ProjectTriggerIntervalSeconds)" buildCondition="ForceBuild" />
<!--<triggerFirstTime>True</triggerFirstTime>-->
</projectTrigger>
</cc:define>
<!-- SourceControl: SVNSourceControl -->
<cc:define name="SVNSourceControl">
<sourcecontrol type="svn">
<trunkUrl>$(SVNTrunkURL)</trunkUrl>
<executable>$(SVNPath)</executable>
<workingDirectory>$(CIDirectory)$(WorkingDirectory)</workingDirectory>
<username>$(SVNUsername)</username>
<password>$(SVNPassword)</password>
<revert>True</revert>
<cleanCopy>$(SvnCleanCopy)</cleanCopy>
<cleanUp>True</cleanUp>
<deleteObstructions>True</deleteObstructions>
</sourcecontrol>
</cc:define>
<!-- Tasks: Replace/Build/Publish/Copy/RunTest -->
<cc:define name="Replace">
<exec>
<executable>$(FCReplacerPath)</executable>
<buildArgs>
<cc:Files/>
<cc:Rules/>
</buildArgs>
<buildTimeoutSeconds>$(ReplaceTimeout)</buildTimeoutSeconds>
<successExitCodes>0</successExitCodes>
</exec>
</cc:define>
<cc:define name="Build">
<msbuild>
<executable>$(MsBuildPath)</executable>
<buildArgs>/t:build /p:configuration=$(BuildConfiguration)</buildArgs>
<cc:MsBuildXmlLogger/>
<workingDirectory>$(CIDirectory)$(WorkingDirectory)</workingDirectory>
<projectFile>$(SolutionPath)</projectFile>
</msbuild>
</cc:define>
<cc:define name="Publish">
<msbuild>
<executable>$(MsBuildPath)</executable>
<workingDirectory>$(CIDirectory)$(WorkingDirectory)</workingDirectory>
<projectFile>$(SolutionPath)</projectFile>
<buildArgs>
/t:ResolveReferences;Compile
/t:_CopyWebApplication
/p:Configuration=$(PublishConfiguration)
/p:WebProjectOutputDir=$(CIDirectory)$(To)
/p:OutputPath=$(CIDirectory)$(To)in
</buildArgs>
</msbuild>
</cc:define>
<cc:define name="Copy">
<exec>
<executable>$(DFCopierPath)</executable>
<buildArgs>
/from=$(CIDirectory)$(From)
/to=$(CIDirectory)$(To)
</buildArgs>
<buildTimeoutSeconds>$(CopyTimeout)</buildTimeoutSeconds>
<successExitCodes>0</successExitCodes>
</exec>
</cc:define>
<cc:define name="RunTest">
<exec>
<executable>$(MSTestPath)</executable>
<baseDirectory>$(CIDirectory)$(WorkingDirectory)</baseDirectory>
<buildArgs>/testcontainer:$(TestContainerPath)</buildArgs>
<buildTimeoutSeconds>$(TestTimeout)</buildTimeoutSeconds>
</exec>
</cc:define>
<!-- 通用配置(模板) -->
<cc:define name="Project">
<project name="$(ProjectName)" queue="$(Queue)" queuePriority="$(QueuePriority)">
<webURL>$(CIWebSite)server/local/project/$(ProjectName)/ViewProjectReport.aspx</webURL>
<!--标签-->
<labeller type="dateLabeller"/>
<artifactDirectory>$(CIDirectory)$(ArtifactDirectory)</artifactDirectory>
<!--项目的目录-->
<workingDirectory >$(CIDirectory)$(WorkingDirectory)</workingDirectory>
<!--自动运行时间间隔-->
<triggers>
<cc:TriggerList/>
</triggers>
<!--对源码修改延迟处理时间间隔-->
<modificationDelaySeconds>$(ModificationDelaySeconds)</modificationDelaySeconds>
<maxSourceControlRetries>5</maxSourceControlRetries>
<!--源代码管理(SVN)-->
<cc:SourceControl/>
<state type="state" directory="$(CIDirectory)$(StateDirectory)"/>
<publishers>
<modificationHistory onlyLogWhenChangesFound="true"/>
<statistics/>
<xmllogger/>
<artifactcleanup cleanUpMethod="KeepLastXBuilds" cleanUpValue="$(KeepLastXBuilds)" />
<!--邮件通知-->
<cc:EmailPublisher/>
</publishers>
<tasks>
<cc:TaskList/>
</tasks>
</project>
</cc:define>
<!-- 默认配置 -->
<cc:define QueuePriority="1"/>
<cc:define KeepLastXBuilds="300"/>
<!-- Email 相关 -->
<cc:define EmailDomain="company.com"/>
<cc:define EmailSendHost="smtp.live.com"/>
<cc:define EmailSendPort="25"/>
<cc:define EmailUseSSL="TRUE"/>
<cc:define EmailSendUser="ci"/>
<cc:define EmailSendPassword="******"/>
<cc:define CIEmailUser="ci"/>
<cc:define PMEmailUser="pm_1"/>
<cc:define name="EmailTesterList">
<user group="tester" name="tester_1" address="tester_1@$(EmailDomain)"/>
<user group="tester" name="tester_2" address="tester_2@$(EmailDomain)"/>
</cc:define>
<!-- Trigger 相关 -->
<cc:define ModificationTriggerIntervalSeconds="300"/>
<cc:define ModificationDelaySeconds="60"/>
<cc:define ProjectTriggerIntervalSeconds="30"/>
<cc:define ProjectTriggerStatus="SUCCESS"/>
<cc:define DailyBuildTriggerTime="18:00"/>
<!-- SVN 相关 -->
<cc:define SVNUsername="ci"/>
<cc:define SVNPassword="******"/>
<cc:define SvnCleanCopy="FALSE"/>
<cc:define name="SourceControl">
</cc:define>
<!-- Task 相关 -->
<cc:define TestTimeout="1800"/>
<cc:define CopyTimeout="60"/>
<cc:define ReplaceTimeout="60"/>
<cc:define BuildConfiguration="DEGUB"/>
<cc:define PublishConfiguration="RELEASE"/>
<!-- TestProject: Service 项目 -->
<queue name="TestProject" duplicates="ApplyForceBuildsReAdd" />
<cc:Project ProjectName="TestProject-Servcies"
Queue="TesProjectt"
WorkingDirectory="TestProjectSourceCodeservice_trunk"
ArtifactDirectory="TestProjectArtifactservice"
StateDirectory="TestProjectState"
PublishDirectory="TestProjectPublish"
EmailSubject="[CI-TestProjectService]"
ModificationDelaySeconds="30"
TeamLeader="caihaihua">
<cc:define name="SourceControl">
<cc:SVNSourceControl SVNTrunkURL="https://vm-caihaihua/svn/testproject/services/trunk/"
SvnCleanCopy="False"/>
</cc:define>
<cc:define name="TriggerList">
<cc:ModificationTrigger/>
<cc:DailyBuildTrigger/>
</cc:define>
<cc:define name="TaskList">
<!-- Config -->
<cc:Replace>
<cc:define name="Files">
/file=$(CIDirectory)$(WorkingDirectory)WcfServiceWeb.config
/file=$(CIDirectory)$(WorkingDirectory)ManagerTestApp.config
/file=$(CIDirectory)$(WorkingDirectory)WcfServiceTestApp.config
</cc:define>
<cc:define name="Rules">
/from=Catalog=(?![^;s']*_log[;s']+)(=?[^;s']*)/to=Catalog=service_trunk_ci
/from=Source=(=?.*?)(=?[;s']+)/to=Source=localhost$2
/from=http://(=?[^/]+)/(=?[^/]+).svc/to=http://vm-caihaihua/testproject_trunk_ci/WcfService/$2.svc
/from=http://(=?.*?)/(?!SSO)(=?[^/]+)/(=?[^/]+).svc/to=http://vm-caihaihua/testproject_trunk_ci/WcfService/$3.svc
/from=http://(=?.*?)/SSO/(=?[^/]+).svc/to=http://vm-caihaihua/testproject_trunk_ci/SSO/$2.svc
</cc:define>
</cc:Replace>
<!-- Build -->
<cc:Build SolutionPath="TestProjectService.sln"/>
<!-- Publish -->
<cc:Copy From="$(WorkingDirectory)WcfService"
To="$(PublishDirectory)WcfService"/>
</cc:define>
</cc:Project>
<cc:Project ProjectName="TestProjectServcies-Test"
Queue="TestProject"
QueuePriority="2"
WorkingDirectory="TestProjectSourceCodeservice_trunk"
ArtifactDirectory="TestProjectArtifact est"
StateDirectory="TestProjectState"
EmailSubject="[CI-TestProjectServcie-Test]"
ModificationDelaySeconds="30"
TeamLeader="caihaihua">
<cc:define name="TriggerList">
<cc:ProjectTrigger ListenToProject="TestProject-Servcies"/>
</cc:define>
<cc:define name="TaskList">
<!--Run Test-->
<cc:RunTest TestContainerPath="ManagerTestinDebugManagerTest.dll"/>
<cc:RunTest TestContainerPath="WcfServiceTestinDebugWcfServiceTest.dll"/>
</cc:define>
</cc:Project>
<!-- TestProject: AdminWeb 项目 -->
<cc:Project ProjectName="TestProject-AdminWeb"
Queue="TestProject"
WorkingDirectory="TestProjectSourceCodeadmin_trunk"
ArtifactDirectory="TestProjectArtifactadmin"
StateDirectory="TestProjectState"
PublishDirectory="TestProjectPublish"
EmailSubject="[CI-TestProject-AdminWeb]"
TeamLeader="caihaihua">
<cc:define name="SourceControl">
<cc:SVNSourceControl SVNTrunkURL="https://vm-caihaihua/svn/testproject/adminweb/trunk/"/>
</cc:define>
<cc:define name="TriggerList">
<cc:ModificationTrigger/>
<cc:DailyBuildTrigger/>
</cc:define>
<cc:define name="TaskList">
<!-- Config -->
<cc:Replace>
<cc:define name="Files">
/file=$(CIDirectory)$(WorkingDirectory)AdminWebWeb.config
</cc:define>
<cc:define name="Rules">
/from=http://(=?[^/]+)/(=?[^/]+).svc/to=http://vm-caihaihua/testproject_trunk_ci/WcfService/$2.svc
/from=http://(=?.*?)/(?!SSO)(=?[^/]+)/(=?[^/]+).svc/to=http://vm-caihaihua/testproject_trunk_ci/WcfService/$3.svc
/from=http://(=?.*?)/SSO/(=?[^/]+).svc/to=http://vm-caihaihua/testproject_trunk_ci/SSO/$2.svc
</cc:define>
</cc:Replace>
<!-- Build -->
<cc:Build SolutionPath="AdminWeb.sln"/>
<!-- Publish -->
<cc:Publish SolutionPath="AdminWebAdminWeb.csproj"
To="$(PublishDirectory)AdminWeb" />
</cc:define>
</cc:Project>
</cruisecontrol>