用 Teamcity 实现 .Net 平台下的持续集成 - 构建
本文中,我将以自己的一个项目作为例子,说明如何在 TeamCity 中定义并执行代码构建。
我使用的例子为自己开发的Shuhari.Framework。这是我从自己开发的项目中提炼出来的一个小型 .Net 类库,包含了方便项目开发的一些常用方法封装。你不必关心它的具体内容,我们只是把它作为一个例子来说明构建方法。
进入构建界面的方法有两个。一个是从右上角 Administration 然后选择 Project; 另一个更简便的途径是直接点左上角的 Projects 链接。无论哪种途径,你都会看到一个 Create project 的大按钮,点击该按钮则来到如下界面。
你会看到,TeamCity 提供了好几种创建项目的方式。由于我们这里的目的是学习,所以请选择 Manually,把基本的步骤搞清楚。等你熟悉以后,可以针对项目来源选择其他对应的快捷方式。
如图中所示,手动创建项目需要输入项目的名字(可以使用几乎任何字符,包括中文)。Project ID 主要用于 URL 地址,当你输入名字的时候会自动匹配,一般不需要手工修改。完成后点击 Create,来到如下的项目配置页面。
一个项目有很多内容可以配置。不过,在绝大多数情况下,项目是从某个代码仓库(SVN / Git)获得的,所以我们先配置版本信息。在下图界面中点击 Create VCS Root。
不同的版本控制系统有不同的配置选项。这是一个托管于 Github 的项目,所以我们首先在版本类型中选择 Git,然后在下面的选项中提供仓库的地址、用户名和密码(如果是开源项目的话,也可以用匿名用户喇取代码,无需用户名)。
选项中有一个 VCS Root Name 是必填项,这是因为 TeamCity 很灵活,允许一个项目中存在多个 VCS。对于只需要单个 VCS 的简单项目,我的习惯是命名为 Default。你可以按你的习惯去设置。
你也可以点击下方的 Show advanced options看看有哪些高级选项,比如设定特殊分支和拉取方式等。填写好信息后,建议先点一下 Test connection 按钮,检查连接信息是不是正确。如果出现绿色提示 Connection successful,你就可以保存设置了。
保存设置以后将回到项目页面。在这里你可以点击 Create build configuration 创建一个构建设置(Build configuration)。不过,这里的概念容易把初学者弄糊涂(我也被绕晕过),所以我先讲一下什么是 Build Configuration。
在 TeamCity 里,一个 Project 通常对应于一个源代码仓库,但对于这个项目可以有多种构建方式。我们最熟悉的、由程序员自己编译的方式有个名称叫开发者构建(Developer build),相应的也存在其他方式,比如大家应该都听说过的夜间构建(Daily Build)。Shuhari.Library 也使用了一个额外的 Build configuration,用来生成文档——因为这个步骤比较慢,且文档并不需要时刻保持最新,我们不希望它拖慢代码构建的速度。不要忘记,持续集成主要的目的之一就是尽快得到反馈信息,如果构建过程太慢的话,人们就不会再愿意去用它了。
点击 Create build configuration 将来到如下界面。这个界面和 Create project 的页面太相似了,所以请看清页面的标题,不要弄错。同样的,对于只有单个构建设置的项目,我习惯把它命名为 Default(请你按自己的习惯命名)。
创建 Build configuration 后将转到它自己的配置界面。首先我们需要指定该配置对应哪个版本库。因为该项目只存在一个版本库,所以我们简单的执行一下 Attach 把他们关联起来即可。如果是其他方式创建的项目,那么你可能需要用下面的 Create VCS Root 新建一个版本库配置,再执行关联。
按照界面的顺序,接下来应该指定 Build step,但我们先从简单的做起,首先指定 Trigger。
什么是 Trigger? Trigger 和数据库里的概念差不多,定义在什么情况下触发 Build configuration 的构建工作。对于开发者构建来说,最常用的当然是 VCS Trigger,它在版本库有新提交的情况下会自动触发构建动作。其他触发器比较常用的还有 Schedule trigger,顾名思义,它可以按时间间隔触发构建(比如每天夜里触发),以实现类似夜间构建之类的功能。
这里我们选择 VCS Trigger 就好了,下面的选项一般无需去关心。
下面来到整个项目的重头戏:Build Step。
什么是 Build step?就是构建过程中要执行的一系列步骤。假如你熟悉 MsBuild 的话,你可以把 Build Step 理解为某个 Target 下顺序执行的一系列 Action。当然,每个项目需要执行的步骤要看这个项目的具体要求,不过下列步骤是大多数项目都需要的:
- Nuget installer (还原项目引用的 Nuget 包。这就是为什么我们上一篇中首先配置 Nuget 工具的原因,如果你的项目不使用 Nuget 来管理的话,那么这一步也可以忽略)
- 编译(不解释)
- 单元测试(似乎也不用解释了)
除此之外,很多项目也希望有代码质量检查之类的功能。.Net 程序员都知道 FxCop / StyleCop 之类的工具,TeamCity 也支持它们,除此之外 TeamCity 也提供了自己的代码质量分析工具。我们下面的步骤会提到它们。
在下图中选择 Add build step。(Auto-detect build step 会试图签出你的整个项目,然后分析文件项目文件并自动生成 build step,但这个过程相当慢,且生成的步骤经常不能准确反映我的意图,所以我通常不会使用它。懒人可以自己尝试一下)。
我们这个示例工程也使用 Nuget 来管理包,所以第一步是还原 Nuget 包。在步骤类型中选择 Nuget installer,并选择解决方案文件的位置。该步骤会查找解决方案中所有包含的 package.config的项目,并还原 Nuget 包。
我对 TeamCity 非常赞赏的一点是,它虽然界面朴实无华,但在细节上做的相当细致。比如选择解决方案这个步骤,你可以点击输入框右边的树状图标,从出现的文件列表中选择,避免了手工输入的痛苦和可能的错误。其他 CI 软件很少有把用户体验做到这种程度的。(由于空间的关系,选择文件的界面放在 Build 的步骤了,请参见下图)。
接下来指定编译步骤。这里有一点讨厌的地方需要说明。按照持续集成的理念,构建服务器上除了安装基本的构建工具外,不应当安装其他复杂的软件特别是 IDE,这和自动化的观点是背道而驰的。但对于 .Net 项目特别是基于 .NetFramework的项目,有的文件会和 Visual Studio 绑在一起,以至于你如果不安装 VS 的话有时候根本无法编译(.Net Core 情况应该会好一些,但 TeamCity 是否适用于 .Net Core,我没有尝试过)。我曾经试过在不安装 VS 的机器上编译 .Net 项目,发现有文件缺失的提示就把自己机器上的文件拷过去,这个工作就几乎花了一下午时间。实际上在构建机器上不安装 VS 而安装独立的开发包也是可以的,但同样非常麻烦。这也是我对 .Net 平台持批评态度的原因之一。总之,对于想在 .Net 平台上做持续集成的同学来说,最简单的解决办法还是在构建服务器上安装 VS(或者TFS)。这是一个挺无奈的事情。
好了,回到正题上来。编译方法其实也有两种选择,MsBuild 和 Visual Studio。其实用 Visual Studio 的话就相当于在后台跑了一个 VS 实例去编译项目。理论上你应该用 MsBuild,但如果必须在服务器上安装 VS 的话,其实都没多大区别。这里我们选择 MsBuild。注意 MsBuild 2015 应该对应 Tools Version 14.0,MsBuild 2017 对应 Tools Version 15.0,如果不匹配的话在编译时可能出现问题。
下一个步骤是单元测试。示例工程是用 NUnit 进行单元测试的,对于 NUnit 2.x 系列进行单元测试是很简单的,但 NUnit 3.x 似乎是因为架构原因,必须自行指定 console tool。比较简单的方法是在项目中引用 NUnit.ConsoleRunner 包,并配置工具路径为 packages\NUnit.ConsoleRunner.3.5.0\tools\nunit3-console.exe (如果版本有变化的话请自行修改)。再多说一句,如果构建服务器上没有安装 VS 的话,那么要用 MSTest 来测试也不是一件简单的事情,请做好安装 VS 或浪费大量时间尝试的准备。
另外,在单元测试的同时你也可以检查测试代码的覆盖情况。TeamCity 默认集成了 dotCover,你也可以指定 NCover 或 PartCover 作为覆盖率工具,只要选择 Coverage tool 即可:
单元测试配置完毕后,我们再添加一些代码检查任务。我一般会首先启用重复代码检查(毕竟,重复是最大的坏味道)。指定构建类型为 Duplicate Finder (.Net) 即可。但有些文件,比如 AssemblyInfo.cs 或设计器生成的文件也存在很多重复代码,但我们一般不认为这是问题,这种情况下你可以在 Excludes 里面把这些文件排除掉。
然后是代码问题分析工具。这实际上是 ReSharper 的一个命令行版本,也集成在 TeamCity 中,可以检查出大量的代码问题(有时候也多得让人有点受不了)。通常你只需要指定解决方案文件的位置,其他设置保持默认即可。
作为示例,我们最后再添加一个生成文档的步骤。实际中我通常会为文档生成创建一个独立的 Build configuration,但这里主要想说明其他一些问题。每个 Build step 都有一个名字,默认情况下这个名字是根据类型自动生成的,比如 MsBuild 的步骤名字也是 MsBuild。但我们在多个步骤中都要使用 MsBuild 来构建,为了区分,这里可以给它起一个明确的名字。
现在我们的步骤已经指定完毕。如果你发现某些步骤的先后顺序弄错了,那么可以在 Build step 的页面选择 Reorder build steps,拖动步骤左边的小把手来重新排列它们:
好了,开始构建!点击左上角的 Projects 回到首页,你可以看到新创建的项目:
因为我们没有签入新的代码,所以构建动作不会自动触发。可以点击右边的 Run 来强制执行构建,稍等片刻你会看到构建开始了:
如果构建失败了,那么 TeamCity 会用红字提示你。界面部分的下拉菜单还提供了更多技术细节,比如这里:
点击 More details 会看到更详细的信息。TeamCity 对细节的执着同样体现在这里,如果你在编译时查看这个界面,会看到输出信息跟着构建进度不断更新,文字信息还会自动分组,可以点开或关闭,而并不需要你手工刷新页面。
这次构建出错的原因是因为生成文档需要使用 SandCastle,而目前这台机器上并未安装它。对于这种情况,我们可以暂时禁用某个步骤。点击项目右边的小三角,选择 Edit Settings,回到设置界面。然后在最后一个步骤右边的下拉菜单中选择 Disable build step:
设置完之后,再次编译项目就可以成功了。成功构建的信息会在面板上显示出来,还允许你深入查看本次构建的各种技术细节。下面我们对主要的输出信息加以说明。
输出信息的 Build Log 部分包含了本次构建的全部日志信息。如果构建出错了,那么你应当首先检查这里的信息以确定原因。
Tests 部分包含了构建中执行的所有单元测试。告诉你一个小技巧,你可以按 Duration 排序所有测试,前几名就是执行时间特别长的测试,我有好几次从这里发现了有性能缺陷的代码。
Code Coverage 显示你的单元测试代码覆盖率(当然前提是你配置了覆盖工具)。我的项目语句覆盖率为 76%,还算可以但也不能说是多么优秀的成绩。
Code Inspection 就是 Resharper 命令行工具检查出的、你代码中可能存在的潜在问题。这里的信息有时会多得惊人,你需要花点时间去慢慢摸索。一点提示:如果你安装了 Visual Studio 的 TeamCity 插件(在用户信息的 My Settings & Tools 页面可以找到下载连接),那么你在这里点击某个问题的链接会直接打开 VS 并定位到有问题的代码。
此外,如果你点击项目下面的 Build configuration (注意不是各个单独的 Build),还会看到一个额外的 Statistics 面板,显示项目按照构建历史中各个指标的变化情况。这里因为只执行了两次且代码没有任何变化,所以看不出什么信息。在实际项目开发中,代码不断变化,那么你会看到一些非常漂亮的图表,涵盖构建时间、测试数量、代码覆盖率、重复代码量、问题数量等众多指标,很有尽在掌握的感觉。
好,构建方法到这里就讲完了。TeamCity 还有很多高级功能如分布式构建、多级项目、自定义构建方法等这里没有涉及,但 TeamCity 的界面设计相当直观(只要你理解了基本概念),自行摸索一下也很容易上手,有需要的同学不妨大胆尝试,或阅读官方文档。
全文目录
- 用 Teamcity 实现 .Net 平台下的持续集成 - 目录
- 用 Teamcity 实现 .Net 平台下的持续集成 - 安装
- 用 Teamcity 实现 .Net 平台下的持续集成 - 配置
- 用 Teamcity 实现 .Net 平台下的持续集成 - 构建 (本篇)