目录
在使用连续集成 (CI) 系统时,团队成员很早就开始集成应用程序组件,并且常常每个开发人员一天就有多次集成。为避免您生厌,在此声明:自动构建会立即检查并确认每个集成,以及时找出所有错误。尽管 CI 这一概念还需要一个熟悉的过程,它却是个非常有价值的系统。
CI 系统的一个基本原理是需重新定义“构建”(Build) 这个词。之前,您可能就把构建简单地想成编译。但对于 CI,您必须认识到构建的定义现在包括验证和测试的所有重要步骤。测试是 CI 的重要支柱。没有测试,只能算是连续编译。
由于开发人员是 CI 系统的主要用户,所以他们是系统成功与否的评判。开发人员实际需要的只是两件事:非常省时的构建和轻松找出构建失败原因的一种方法。成功还取决于团队的构建习惯。建议遵守以下简单的原则:
- 切勿在中断构建后置之不理。
- 切勿提交到中断的构建。
- 心地善良。如果发现构建中断,请查看构建报告。如果知道如何修复问题,请联系中断构建的开发人员并提供帮助。
降低采用成本
我最近感觉,越来越多的开发环境开始将 CI 用作最佳实践,并且如果采用成本得以降低,这个队伍还会扩大。一个资深的程序员需要几天才能建立并运行一个基本 CI 服务器;如果是新手,则可能花上一周时间。
令人沮丧的是:准备 CI 服务器所做工作与服务器所完成的工作有 80% 完全相同 — 完成上一构建后执行清理、获取最新的源代码、增加版本、编译、运行测试、执行静态分析以及创建安装程序。CI 服务器实例之间的差异少之又少。这一通用性是降低连续集成采用成本的关键所在。
在本文中,我将探讨设置和使用 CI 服务器所需的工作以及有助于完成工作的各种解决方案。首先,我会使用 CruiseControl.Net、MSBuild 2.0、MbUnit 和 WatiN 手动创建一个 CI 服务器。接下来,我会向您展示如何使用名为 CI Factory 的开源项目来完成同样的功能(这个方法省力得多)。
我将使用的示例应用程序是开源项目 NunitAsp 中的一个简单访客留言簿。我将编译此应用程序,创建一个 Microsoft Installer (MSI) 安装应用程序,安装它,然后使用 WatiN 进行测试。完成后的访客留言簿应用程序可以保存您的姓名和留言(如
图 1 所示)。
Figure 1
完成后的示例应用程序可保存姓名和留言 (单击该图像获得较大视图)
正如您可能已猜测到的,此应用程序将姓名和留言存储在会话状态中。它的 WatiN 测试非常简单(如
图 2 所示)。
Figure 2 Testing the Sample App
|
[Test]
public void Register()
{
Ie = new IE("about:blank");
Ie.GoTo("http://localhost/GuestBook/GuestBook.aspx");
Ie.TextField(Find.ByName("name")).TypeText("Jay");
Ie.TextField(Find.ByName("comments")).TypeText("Hello");
Ie.Button(Find.ByName("save")).Click();
Assert.AreEqual("Jay", Ie.TableCell(Find.By("innerText", "Jay"))
.Text, @"innerText does not match");
Assert.AreEqual("Hello", Ie.TableCell(Find.By("innerText",
"Hello")).Text, @"innerText does not match");
}
|
WatiN 是基于 Internet Explorer
® 的一个 Microsoft
® .NET 测试框架。它会打开一个新的 Internet Explorer 实例并驱动测试。在测试中输入姓名 Jay 和留言 Hello,单击保存按钮,然后检查访客表中是否包含预期的内容。
访客留言簿应用程序未附带安装程序。我通过向解决方案添加一个 Visual Studio
® Web 安装项目来创建了一个安装程序。然后,将访客留言簿项目的主要输出添加到 bin 文件夹,将文件 Global.asax、GuestBook.aspx、和 web.config 添加到 Web 应用程序文件夹。这样就构建了一个安装程序!
因此形成一个包含以下三个项目的解决方案:生产应用程序访客留言簿、测试项目 GuestBook.Test 和部署项目 GuestBookSetup。如果使用 CI 系统来构建它...
入门
从开发人员的角度出发,我发现将 CI 视为基于任务的开发非常有用。完成一个任务后就提交给构建。让我们再进一步,谈谈任务大小。任务是可在一天或更短时间内完成的事情。任务是完成后按工作顺序离开系统的事情。此外,任务通常小于需求。
设置 CI 服务器时需执行多个任务。先来介绍一下将托管 CI 服务器的物理服务器。首先需在 CI 服务器上安装开发环境。它可能包括集成测试(如 Microsoft SQL Server
®)所需的外部依赖关系以及编译所需的控件或其他运行时依赖关系。开发环境的其余部分包含开发目录树。Tree Surgeon 是一种用于创建开发树的免费工具 (
codeplex.com/treesurgeon)。树中应有明显的位置用于存放构建脚本、生产源代码、测试源代码和第三方依赖关系。定义完树后,就可以开始编写构建脚本(请参见
图 3)。
Figure 3 CI Build Script
|
<PropertyGroup>
<ProjectName>GuestBook</ProjectName>
<RootDirectory>c:\Projects</RootDirectory>
<ProjectRootDirectory>$(RootDirectory)\$(ProjectName)
</ProjectRootDirectory>
<ProjectCodeLineDirectory>$(ProjectRootDirectory)\Trunk
</ProjectCodeLineDirectory>
<BuildDirectory>$(ProjectCodeLineDirectory)\Build</BuildDirectory>
<SourceDirectory>$(ProjectCodeLineDirectory)\Source</SourceDirectory>
<ThirdPartyDirectory>$(ProjectCodeLineDirectory)\Third Party
</ThirdPartyDirectory>
<UnitTestDirectory>$(SourceDirectory)\Unit Test</UnitTestDirectory>
<InstallDirectory>$(SourceDirectory)\Install</InstallDirectory>
</PropertyGroup>
|
一般情况下,不应尝试使用外部依赖关系。这样开发环境相对简单。如果没有外部依赖关系,新开发人员需要做的就是复制服务器上源代码中的 dev 树。这是最简单的情况。然而,大部分项目都包含一些外部依赖关系,因此,应在安装脚本中系统化所有外部依赖关系。检查是否存在依赖关系,如果不存在,即可安装。脚本可从网络上的共享驱动器执行安装,或者从 Web 下载安装程序。
还应在脚本中对外部依赖关系的配置(数据库、IIS、注册表项、环境变量等等)进行编码。其中的关键是用一点点时间对您的开发环境进行编码。而后,如果需设置其他 CI 服务器,则新环境的设置将变得非常简单。
构建和测试
此时,我已为管理外部依赖关系、编译以及执行单元测试的基本构建脚本奠定了基础,如下所示:
|
<Target Name="Build">
<CallTarget Targets="SetupVirtualDirectory"/>
<CallTarget Targets="Compile"/>
<CallTarget Targets="RunTests"/>
</Target>
|
第一个目标 SetupVirtualDirectory(请参见
图 4)是将 IIS 中的 Web 应用程序项目目录映射为虚拟目录。它是此项目的唯一外部依赖关系。并且,SetupVirtualDirectory 还可使开发人员避免执行部署 Web 应用程序这一耗时的步骤。稍后,当我在 CI 服务器上执行此脚本时,我会实际执行完全安装。
Figure 4 Targeting SetupVirtualDirectory
|
<Target Name="SetupVirtualDirectory">
<Exec
WorkingDirectory="$(SystemRoot)\System32\inetsrv\APPCMD.EXE"
Command='Add APP /site.name:"Default Web Site"
/path:/GuestBook/physicalPath:$(SourceDirectory)
\Production\GuestBook' />
<Exec
WorkingDirectory="$(SystemRoot)\System32\inetsrv\APPCMD.EXE"
Command='Set APP "Default Web Site/GuestBook"
/virtualDirectoryDefaults.physicalPath:$(SourceDirectory)
\Production\GuestBook' />
</Target>
|
编译目标也非常简单。它会扫描 Source 目录检查有无任何 C# 项目并编译它们:
|
<ItemGroup>
<Projects Include="$(SourceDirectory)\**\*.csproj" />
</ItemGroup>
<Target Name="Compile">
<MSBuild Projects="@(Projects)" Targets="Build"
StopOnFirstFailure="false" ContinueOnError="false">
</MSBuild>
</Target>
|
共有两个 C# 项目 — 应用程序项目和测试项目:
|
C:\Projects\GuestBook\Trunk\Source\
Unit Test\GuestBook.Test\GuestBook.Test.csproj
C:\Projects\GuestBook\Trunk\Source\Production\
GuestBook\GuestBook.csproj
|
请注意,尽管此方法无需更改构建脚本就可添加新项目,但是您却无法控制构建项目的顺序。如果项目之间存在依赖关系,则会产生一个重大问题。如果需要控制构建顺序,请在 ItemGroup 中明确列出项目。
为运行单元测试,我会通过其相关的 MSBuild 任务使用 MbUnit(请参见
图 5)。它将程序集列表作为参数,并且执行其中的所有测试。脚本已设置为扫描 Unit Test 目录的 bin 目录中匹配格式 *.Test.dll 的所有文件。只要开发人员遵守这一约定,则对于其他测试项目无需更改构建脚本。
Figure 5 Running Unit Tests with MbUnit and MSBuild
|
<UsingTask
TaskName="MbUnit.MSBuild.Tasks.MbUnit"
AssemblyFile="$(ThirdPartyDirectory)\MbUnit\MbUnit.MSBuild.Tasks.dll"
/>
<ItemGroup>
<TestAssemblies Include="$(UnitTestDirectory)\**\bin\**\*.Test.dll" />
</ItemGroup>
<Target Name="RunTests">
<MbUnit
Assemblies="@(TestAssemblies)"
ReportTypes="Xml"
ReportFileNameFormat="unittest"
ReportOutputDirectory="$(BuildDirectory)\UnitTestReport"
/>
</Target>
|
名为 GuestBook.msbuild 的这个脚本位于 Build 目录中,我还在 Source 目录中放了一个批处理文件 Build.bat。开发人员将来可能要离开 Source 目录,所以在此处放置一个批处理文件便于他们执行构建脚本:
|
call "%ProgramFiles%\Microsoft Visual Studio 8\VC\vcvarsall.bat"
msbuild ..\Build\GuestBook.msbuild /t:Build
SET /P variable="Hit Enter to exit."
|
在 CI 服务器上运行构建脚本时,我希望在更加真实的环境中完成测试。通过执行安装程序即可轻松实现这一目的。访客留言簿解决方案包括一个 Web 部署项目:GuestBookSetup.vdproj。为构建此项目,我需要调用 devenv.exe 而非 MSBuild,因为 MSBuild 目前不支持部署项目:
|
<Target Name="CreateInstaller">
<Exec
WorkingDirectory="$(SourceDirectory)"
Command='"C:\Program Files\Microsoft Visual
Studio 8\Common7\IDE\devenv.exe" $(ProjectName).sln
/Build Debug /project GuestBookSetup' />
</Target>
|
我需要以无提示方式执行安装,因为它是一个完全自动化的构建。最好在编写安装程序前已了解这一特点。在编写完安装程序后再尝试添加此功能则会比较麻烦。在 MSBuild 脚本中,我需要设定安装和卸载的目标,如下所示:
|
<Target Name="InstallApplication">
<Exec
WorkingDirectory="$(InstallDirectory)\GuestBookSetup\Debug"
Command='msiexec /i GuestBookSetup.msi /q'
/>
</Target>
<Target Name="UninstallApplication">
<Exec
WorkingDirectory="$(InstallDirectory)\GuestBookSetup\Debug"
Command='msiexec /x GuestBookSetup.msi /q'
/>
</Target>
|
这些目标使用 Exec 任务和应用程序 msiexec.exe 来执行 MSI 文件(在 PATH 中)。 开关 /i 实现安装,/x 实现卸载,而 /q 则实现无提示。此安装程序包无需用户输入;测试可接受默认设置。然而,可使用 msiexec 来向安装程序包传递参数。
CI 服务器的输入目标类似如下:
|
<Target Name="Triggered">
<CallTarget Targets="UninstallApplication" ContinueOnError="true"/>
<CallTarget Targets="Compile"/>
<CallTarget Targets="CreateInstaller"/>
<CallTarget Targets="InstallApplication"/>
<CallTarget Targets="RunTests"/>
</Target>
|
现在,我已拥有一个可在开发人员机器上执行个人构建并在 CI 服务器上执行公共构建的构建脚本。使用 CruiseControl.Net(更普遍的叫法是 CCNet)来启动这个新的构建脚本。CCNet 安装到 Program Files 目录中,并可被多个不同的项目共享。用于配置 CCNet 的文件有两个:服务器的 ccnet.config 以及仪表板的 dashboard.config。仪表板配置仅用于显示目的,并不参与产品(在此例中为访客留言簿)的创建。因此,它的 .config 文件将不会保存在源代码控制中。
但服务器的 .config 文件对于构建过程而言至关重要。为在源代码控制中管理访客留言簿的 .config,我将使用从 CCNet 服务器的 .config 文件引用的文档类型定义 (DTD) 来创建一个新的 XML 文件并定义配置:
|
C:\Program Files\CruiseControl.NET\server\ccnet.config
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE cruisecontrol [
<!ENTITY GuestBook SYSTEM
"C:\Projects\GuestBook\Trunk\Build\CCNetProject.xml">
]>
<cruisecontrol>
&GuestBook;
</cruisecontrol>
|
源代码控制
让我们添加一个新的维度并引入源代码控制。CI 服务器应执行干净的构建,不仅会删除编译后的产物,还有源文件和测试执行环境。执行脏构建是个不正确的做法。更糟糕的是执行“酵母式”构建。此时需要有之前的构建才能成功执行构建。
请注意,在源代码控制块中,每次构建均会提取全新的程序源代码副本。将 Cleancopy 设为 true,如下所示:
|
<sourcecontrol type="vss" autoGetSource="true" applyLabel="true">
<project>$/GuestBook/Trunk</project>
<username>Build</username>
<password>password</password>
<workingDirectory>c:\projects\GuestBook\Trunk</workingDirectory>
<executable>C:\Program Files\Microsoft Visual
Studio\VSS\win32\SS.EXE</executable>
<ssdir>c:\Source Safe Databases\GuestBook</ssdir>
<cleanCopy>true</cleanCopy>
</sourcecontrol>
|
大多数源代码控制系统均需要相同类型的配置信息:存储库的位置、凭据、项目根文件夹的路径以及磁盘上的工作文件夹。尽管本文示例使用的是 Visual SourceSafe
® (VSS),对于各种源代码控制提供程序(Visual Studio Team Foundation Server、Subversion、Perforce、Vault 等等),CCNet 中的源代码控制块看起来都类似。
除在构建开始时执行干净的获取外,如果构建成功,它还会在构建结束时创建一个标签。此标签应用于项目节点 — 在本例中为 Trunk。请记住,Trunk 包含构建此版本产品所需的所有内容。因此,如果需在六个月后返回此版本,提取该标签即可提供成功构建此应用程序所需的所有内容。我发现许多团队都忽视了这一点。
许多团队无法将 CI 系统看作其产品的一部分。它可能并非是提供给客户的事物,但它仍在您的交付能力中占据着至关重要的角色。给予其适当的关注并将它包括到源代码控制中(用产品的源代码来为其添加标签)。
此设置的另一优势是不必从构建服务器执行更改。可轻松地从工作站更改 MSBuild 脚本、签入源代码控制以及通过 CCNet 提取到构建服务器。这样就不会在构建服务器上执行更改之后忘记签入这些更改。CCNet MSBuild 任务按如下方式编译解决方案:
|
<msbuild>
<executable>
C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\MSBuild.exe
</executable>
<workingDirectory>
C:\Projects\GuestBook\Trunk\Source
</workingDirectory>
<projectFile>
C:\Projects\GuestBook\Trunk\Build\GuestBook.msbuild
</projectFile>
<buildArgs>
/noconsolelogger /p:Configuration=Debug /v:diag
</buildArgs>
<logger>
C:\...\ThoughtWorks.CruiseControl.MSBuild.dll
</logger>
</msbuild>
|
记录和报告
MSBuild 和 CCNet 均未附带 XML 记录程序。可从 CCNet 维基下载一个。此处是从 Build 目录引用的。XML 日志对于 CCNet 而言非常重要,因为仪表板 UI 会根据构建的 XML 输出应用 XSLT,以产生 HTML。MSBuild 任务会收集 XML,并将它包括到每个构建的 CCNet XML 日志文件中。要在仪表板上呈现格式精美的 MSBuild 报告,需向 dashboard.config 文件(位于 C:\Program Files\CruiseControl.NET\webdashboard\dashboard.config)添加一个配置项。在位置 /dashboard/plugins/buildPlugins 中添加此节点,如以下代码所示:
|
<xslReportBuildPlugin description="MSBuild Output"
actionName="MSBuildOutputBuildPlugin"
xslFileName="xsl\msbuild.xsl" />
|
回过头来看看构建脚本目标 RunTests,请注意,单元测试执行在目录 \Trunk\Build\UnitTestReport 中发布了一个 XML 报告。需包含此 XML 才能构建单元测试报告。然后,CCNet 将文件合并发布程序块添加到 ccnet.config 文件,从而将它合并到最终构建日志中,以供在 Web 仪表板上报告之用。
|
<merge>
<files>
<file>c:\projects\GuestBook\Trunk\Build\
UnitTestReport\unittest.xml</file>
</files>
</merge>
|
可在以下两个位置中显示 MbUnit 的单元测试报告:摘要构建报告和详细报告。应将详细报告元数据添加到 /dashboard/plugins/buildPlugins 中的 dashboard .config 文件中,如下所示:
|
<xslReportBuildPlugin
description="Unit Details"
actionName="UnitDetailsBuildReport"
xslFileName="xsl\MBUnitDetails.xsl" />
|
同样地,应将摘要报告添加到位置 /dashboard/plugins/buildReportBuildPlugin/xslFileNames:
|
<xslFile>xsl\MBUnitSummary.xsl</xslFile>
|
触发构建
尽管已完成 CCNet 配置的所有主要部分,我们仍需添加一个触发器来实现构建:
|
<triggers>
<intervalTrigger
seconds="60"
buildCondition="IfModificationExists" />
</triggers>
|
此触发器块可能每 60 秒执行一次构建。CCNet 会询问源代码控制存储库自上次执行构建后有无修改,如果回答是,则会触发构建。
还有许多可添加到 CCNet .config 文件中的配置元素。可发送电子邮件告知您构建结果。可控制每个构建的标签或版本号。还可以实现许多有趣的功能。
运行 CI 服务器
现在,我已配置完 CCNet,完成以下几项事宜后 CI 服务器就将完全可运行:需将所有内容均添加到源代码控制并启动 CCNet 服务器。
根据安装 CCNet 的方式,可通过启动 Windows 服务或通过执行 ccnet.exe 控制台应用程序来打开服务器。如果选择了 Windows 服务,需确保桌面已访问此服务,因为 WatiN 需要它。
要查看仪表板,在浏览器中打开 http://localhost/ccnet。主仪表板项目表格显示了一些有关构建进度的信息。左边有 CCTray 工具的下载链接。要在桌面上使用 CCTray,安装后对其进行配置,这样它才知道构建服务器和您要订阅的项目。然后,此工具将驻留在系统任务栏中,并且当构建服务器的状态和活动发生变化时,它会通知您。它甚至会尝试预计构建的完成时间。
图 6 显示了访客留言簿项目的成功构建报告。
Figure 6
成功构建的 CCNet 报告 (单击该图像获得较大视图)
构建报告包括一个摘要页面和多个详细报告。请注意,左侧有单元测试摘要报告和详细报告的链接。左侧还有 MSBuild 输出详细报告。“查看构建日志”链接显示了此构建实例的原始 XML 日志文件。
使用 CI Factory
现在,我将展示如何利用 CI Factory 来更加省力地达到相同目的或实现更大的功效。下载并安装 CI Factory 后,打开解决方案文件 CI Factory.sln。在“解决方案资源管理器”中打开 Arguments.xml 文件配置新项目。
首先,我将项目名称设置为 GuestBook:
|
<property name="ProjectName" value="GuestBook" />
|
接下来,设置 dev 树的文件夹的名称。我将不会使用默认值,改为对各值进行相应更改以反映我的开发树(如
图 7 所示)。
Figure 7 Setting the Folder Names for the Dev Tree
|
<property name="ProjectsDirectory"
value="c:\Projects"/>
<property name="ProjectCodeLineName"
value="Trunk"/>
<property name="ProductDirectoryName"
value="Source"/>
<property name="ThirdPartyDirectoryName"
value="Third Party"/>
<property name="ProductionDirectoryName"
value="Production"/>
<property name="UnitTestDirectoryName"
value="Unit Test"/>
<property name="InstallDirectoryName"
value="Install"/>
|
现在,设置构建母版页和开发人员电子邮件地址。CI Factory 会对 CCNet 进行配置,以便在开发人员修改的构建完成时通过电子邮件向他们发送一个构建报告。当构建失败时,它还会向构建所有者发送电子邮件,如以下代码所示:
|
<property name="BuildMaster.Email" value="[email protected]"/>
<largeproperty name="Developer.List">
<value expand="true" xml="true">
<user name="MyBrotherDarell"
group="developer"
address="[email protected]"/>
<user name="MyOtherBrotherDarell"
group="developer"
address="[email protected]"/>
</value>
</largeproperty>
|
还可在此文件中设置其他选项(尽管大部分默认值足够我现在使用)。例如,可通过将以下属性设置为 true 来收集 WatiN 测试的覆盖范围统计信息:
|
<property name="NCover.IIS" value="test" />
|
最后,选择构建的工具堆栈(如
图 8 所示)。
Figure 8 Configuring Build Tools
|
<strings id="Packages.InstallList">
<string value="VisualSourceSafe"/>
<string value="CSDiff" />
<string value="SourceModificationReport"/>
<string value="Versioning"/>
<string value="MSBuild" />
<string value="VS.NETDeploy" />
<string value="NCover" />
<string value="DotNetUnitTest"/>
<string value="nDepend"/>
<string value="FxCop"/>
<string value="Simian" />
<string value="Analytics" />
<string value="Alerts" />
<string value="Deployment"/>
<string value="Workspace"/>
</strings>
|
CI Factory 由一个个程序包组成,因此可进行扩展。VisualSourceSafe 程序包提供与源代码控制之间的集成。CSDiff 将在仪表板上提供各种报告。Versioning 将维护程序集和文件的版本信息。MSBuild 将管理编译和报告。NCover 将如何反应呢?相信您已明白我的意思。
此时,我已完成 CI Factory 安装程序的配置,因此我将执行运行批处理文件 C:\Tools\CI Factory\run.bat 以创建访客留言簿项目。脚本将安装缺失的必要软件,并提示接受许可证。
接下来,批处理文件将 CCNet 服务器启动为控制台应用程序,并打开所有构建文件的 Visual Studio 解决方案。我发现让一个项目来专门管理构建脚本非常方便。首先,项目中只有我需要编辑的文件。由于在编写 XML 时有 Visual Studio 编辑器的帮助,也节省了时间。在 Visual Studio 中创建 XML 准确度极高;编辑器可帮助您注意到所出现的错误。还可利用 XSD(XML 架构定义)使编辑器实现的功能:提供 IntelliSense
®!与之相比,记事本并不会报错。
此时,我已安装了一个简单的 CI Factory。所创建的树非常类似于我之前手动创建的树。您可能已注意到 Build 目录中的服务器和仪表板目录。它们包含专门处理访客留言簿项目的 CCNet 副本。相较于之前示例中外部且很可能共享的依赖关系,它们是内部依赖关系。CCNet 项目配置文件位于与之前示例相同的位置中:C:\Projects\GuestBook\Trunk\Build\ccnetproject.xml。
您需要注意的主要不同点是这里只有两个项目 — 一个用于构建访客留言簿产品,一个用于管理构建脚本。构建脚本项目监控源代码控制中的构建目录,仅在触发时从存储库更新磁盘上的文件。构建产品的项目会忽略构建目录中所做的更改。
在之前的示例中,我不得不编写了一个 MSBuild 脚本,但现在,大部分这样的工作在安装过程中完成,且很大程度上是由 Main.Build.xml 脚本来维护。类似于我编写的 MSBuild 脚本,Main.Build.xml 包含名为 triggered 的目标,但它并不包含名为 Build 的目标。Build 目标在名为 Personal.Build.xml 中的脚本中且位于 Source 目录下。
其中的批处理文件比 Build.bat 中的要多。例如,Test.bat 仅执行构建脚本的测试部分。但 Build.bat 包括更多功能,如从源代码控制更新开发人员工作区:
|
..\Build\nant\bin\nant.exe -buildfile:Personal.Build.xml UpdateSource
IF NOT %ERRORLEVEL%==0 exit /B %ERRORLEVEL%
..\Build\nant\bin\nant.exe -buildfile:Personal.Build.xml %1 %2 %3 %4 %5 %6 %7 %8
SET /P variable="Hit Enter to continue."
|
自定义 CI Factory
CI Factory 的默认安装已能满足我的绝大部分需求,但是可通过安装后配置和其他自定义来个性化环境。自定义 CI Factory 时需注意的一点是:它是使用 NAnt 来配置的,因此即便使用 MSBuild 来通过 CCNet 控制构建,仍必须使用 NAnt 的 XML 语法来自定义 CI Factory。
已配置了 CCNet 仪表板,因此无需添加配置信息就能使用各种 XSLT 文件。默认安装甚至配置了 IIS,创建了多个虚拟目录。我稍微更改了一下 dashboard.config (如
图 9 所示):仅更改了主构建报告中显示内容的顺序。我倾向于将部署文件放在报告顶部附近,因为该部分列出了用于下载构建期间创建的各种文件(如 MSI 文件、可执行文件和 ZIP 文件)的超链接。
Figure 9 Customizing the CCNet Build Report
|
<buildReportBuildPlugin imageFileName="images/BuildReport.gif">
<xslFileNames>
<xslFile>xsl\header.xsl</xslFile>
<xslFile>xsl\modifications.xsl</xslFile>
<xslFile>Packages\Deployment\deployment.xsl</xslFile>
<xslFile>xsl\compile.xsl</xslFile>
<xslFile>Packages\VS.NETCompile\Compile.xsl</xslFile>
<xslFile>Packages\MSBuild\compile-msbuild.xsl</xslFile>
<xslFile>Packages\DotNetUnitTest\MbUnitDisplaySummary.xsl</xslFile>
<xslFile>Packages\NCover\NCoverDisplaySummary.xsl</xslFile>
<xslFile>Packages\Simian\SimianDisplaySummary.xsl</xslFile>
<xslFile>Packages\nDepend\nDependSummaryDisplay.xsl</xslFile>
<xslFile>Packages\FxCop\FxCopSummaryDisplay.xsl</xslFile>
</xslFileNames>
</buildReportBuildPlugin>
|
我还对 MSBuild 程序包进行了配置更改。默认情况下,它会针对生产树中所有项目的主输出创建一个 ZIP 文件。在此示例中,只有一个此类项目 — 访客留言簿,因此我不需要 ZIP 文件,MSI 就可以了。通过将程序包中的以下两个属性设置为 false 即可禁用创建 ZIP 文件功能:
|
<property name="Compile.ToCopyToBin" value="false" overwrite="false"/>
<property name="Compile.ToDeployZip" value="false" overwrite="false"/>
|
与我之前编写的 MSBuild 脚本一样,我需要安装和卸载 MSI 文件。因此,我编辑 Build 目录中的 Main.Build.xml 文件,在调用前后添加了 exec 任务以运行单元测试:
|
<exec program="msiexec"
commandline="/i ${Install.MsiSourceDir}\${Install.MsiSourceFile} /q"
verbose="true"/>
<call target="UnitTest.RunTests" />
<exec program="msiexec"
commandline="/x ${Install.MsiSourceDir}\${Install.MsiSourceFile} /q"
verbose="true"/>
|
我还希望维持之前示例的良好开发人员体验,并确保开发人员已在 IIS 中将访客留言簿 Web 应用程序项目目录映射为虚拟目录。我的做法是在工作区程序包中添加一个配置脚本。我在工作区的 Configuration 目录中创建一个名为 IIS 的新目录,并添加新文件 IIS.Script.xml。脚本非常简单,仅包含一个创建虚拟目录的目标:
|
<target name="Workspace.Configuration.IIS">
<mkiisdir
dirpath="${ProductionDirectory}\GuestBook"
vdirname="GuestBook"
defaultdoc="GuestBook.aspx"
enabledefaultdoc="true"
/>
</target>
|
要调用此目标,需向工作区程序包注册它。方法是添加 include 任务并将 IIS 添加到配置脚本列表:
|
<strings id="Workspace.Configuration">
<string value="MbUnit"/>
<string value="IIS"/>
</strings>
<include buildfile="Configuration\IIS\IIS.Script.xml"/>
|
现在,我已拥有一个可正常运行的构建服务器,我将把访客留言簿产品添加到源代码控制并触发构建。
CI Factory 的主仪表板项目表格(请参见
图 10)显示了许多有关当前构建状态和活动的信息(如已用构建时间以及已检测到的更改)。如之前一样,左侧有 CCTray 的下载(尽管此安装程序会为您配置 CCTray)。
Figure 10
仪表板中报告的构建状态 (单击该图像获得较大视图)
完成构建后,可查看版本 1.0.0.1 的报告(如
图 11 所示)。您会发现构建报告中包括有 MSI 文件的链接。
Figure 11
构建报表 (单击该图像获得较大视图)
向下滚动时您会看到来自其他工具的摘要报告,它们是我在安装时选择包括在程序包中的工具:Ncover、Simian 和 FxCop。还可看到其详细报告的链接,包括 Ndepend。这些程序包无需我再进行其他配置。
剩余工作
我在本文中仅展示了所有 CI 系统常用工作的 80%。您可能想知道剩下的 20% 是什么?管理应用程序数据库是一个常见的示例。我使用源代码控制存储库来帮助我的团队保持同步。不管是对于个人使用还是公共构建服务器测试,CI 系统都在同步团队数据库方面起到主导作用。
另一常见示例是系统部署,不仅包括部署测试环境,还包括客户环境的部署。一些最先进且成功的 CI 系统就是直接在客户环境中执行部署它是现在的终极目标 — CI 一直延伸到用户桌面。
在此过程中,还会出现有助于您自由发挥的中间目标,如自动化创建发行说明、将 CI 系统扩展到测试团队的桌面(类似于 build.bat 扩展到开发人员桌面的方式)以及除安装程序外,在发布版本中创建一个虚拟机甚至是一组虚拟机。通用规则是自动化所有能自动化的功能并且常常运行该自动化。
Jay Flowers 是 CI Factory 开源项目的创建者。他撰写了《Windows Developer Power Tools》一书的 CI Factory 一节。Jay 还参与了 MbUnit 项目和其他更小的开源项目。
jayflowers.com 上发布的文章和博客均彰显了他对软件开发的挚爱。