这是持续集成系列的下半部分,如果你还没看过上半部分的话,请点击这里:用MSBuild 和Jenkins 搭建持续集成环境(1)。
与Jenkins 相会
Jenkins 在 2004 年由 Kohsuke Kawaguchi 创建,最开始的名字叫 Hudson。Kawaguchi 当时在 Sun 工作,Sun 直到 2008 年一直为 Hudson 提供支持,但是当它被 Oracle 收购以后,Oracle 和 Hudson 的开发者社区出现了分歧。2011 年,Oracle 宣称了对 Hudson 商标的所有权,Hudson 开发社区于是将 Hudson 改名叫 Jenkins,摆脱了 Oracle 的干扰,继续开发工作。
第一眼看上去,Jenkins 像是一个专为 Java 项目准备的持续集成工具:有为 Maven 项目设计的 job,有诸多为 Java 项目提供的默认插件,更不要说那些用 Java 写的插件了。
但 Jenkins 其实是一个非常灵活的工具,它可以结合各种版本管理系统和构建工具,用来构建任何类型的项目。在这篇文章中,我们会利用它的灵活性,从 Mercurial 中 pull 代码,用 MSbuild 构建项目。首先,我们需要下载并安装 Jenkins,然后安装 Mercurial 和 MSBuild 插件。
配置 Jenkins
从官方网站下载安装程序。它的Windows 安装包很简单,会把Jenkins 安装成Windows 服务。Jenkins 的默认的访问路径是 http://localhost:8080 ,请确保 8080 端口不要被其他应用占用。
安装完毕以后就是装插件。请点击“Manage Jenkins”链接,然后再点击“Manage Plugins”。在“Available”标签页可以查看当前可安装的插件──你需要有一个能上网的环境,才能看到这一页的内容。用 filter 找到 Mercurial 和 MSBuild 插件,点击插件名称前面的单选框,进行安装。你可以点击“Installed”标签页,来确保这两个插件已经被安装成功了。在安装过程中,你可能会看到提示信息说 Jenkins 需要重启才能完成安装,请让它重启,等重启完成后再访问“Installed”标签页,看看是不是安装成功。
Mercurial 插件还需要做一些配置,才能让 Jenkins 找到 Mercurial 的安装路径。回到“Manage Jenkins”这一页上来,点击“Configure System”,找到“Mercurial”这一部分──如果你找不到“Mercurial”的话,就说明 Mercurial 插件没有装好──点击“Add Mercurial”按钮之后,你需要给这个 Mercurial 实例起个名字(自己用着越方便越好);还需要输入 Mercurial 可执行文件的安装路径,这里用的是 hg.exe 文件的所在目录;最后是可执行文件的名字,就用“INSTALLATION/hg.exe”就好,INSTALLATION 这个单词会被前面输入的安装路径替换掉。
创建一个 Jenkins Job
点击“Back to Dashboard”,回到 Dashboard 上来,然后点击“New Job”链接。你会看到一组 job 类型,选择“Build a free-style software project”,给它命名为“HelloCI-RunUnitTests”,点击 OK。
下一步是 job 配置页面。这一页有很多配置项,而且大多数都带有详细的描述信息,点击右侧的帮助图标就可以看到。我们现在只配置两部分,一是代码库所在位置,二是如何用 MSBuild 构建项目。
找到“Source Code Management”,选择 Mercurial。在“Mercurial Version”输入框中输入先前在“Configure System”中配置的 Mercurial 名称。然后在 Repository URL 中输入 Mercurial 版本库的 URL(也可以是一个文件系统的路径)。最后在 Branch 中输入你想跟踪的分支名。
接下来到“Build”这部分。点击“Add build step”按钮后,下拉框中就会出现一系列的 step 类型以供选择,其中便包括“Build a Visual Studio project or solution using MSBuild”,如果你没看到这个选项,就说明 MSBuild 插件没有正确安装。
点击“Build a Visual Studio project or solution using MSBuild”之后,在“MSBuild Build File”输入框中输入构建脚本的名字:HelloCI.msbuild。我们想让 Jenkins 执行“RunUnitTests”这个 Target ,如果你没有把 DefaultTargets 属性设成 RunUnitTests 的话,可以在“Command Line Arguments”中输入“/t:RunUnitTests”,其中 /t 是 /target 的简写。
点击“Save”或“Apply”保存之后,这个 job 一旦被触发,就可以 pull 代码下来,编译项目,执行单元测试。我们先来手工触发一次,看看配置是否正确。先回到 Dashboard,这时可以在屏幕中央看到我们的 job。点击 job 名字,然后在左侧的链接中找到“Build Now”链接,点击它,Jenkins 就会开始执行。在这组链接的下方有一个“Build History”列表,它显示的是这个 job 的所有构建历史,当第一次构建开始运行的时候,你会在列表中看到一个进度条,同时还有一个小圆球显示构建状态。圆球闪烁表示构建正在进行中,它停止闪烁的时候一般会是红色或蓝色,红色表示构建失败,蓝色表示成功。
如果这个 job 能够访问 Mercurial 版本库,找到了 HelloCI.msbuild 脚本,“RunUnitTest”执行成功,这个圆球应该会变蓝。这时候你也就顺利完成了第一个 Jenkins 构建。如果构建失败,请点击“Build History”对应的编号查看详细信息,然后点击“Console Output”,就可以看到 Jenkins 所执行的每一个命令和对应结果,从中可以分析出构建失败的原因。
触发构建
构建成功以后,下一步要做的就是让 Jenkins 检测版本库的变化,一旦有代码提交,Jenkins 就要 pull 代码并执行构建。有好几种方法可以做到这一点。最简单的就是让 Jenkins 定时构建,但是如果在这一段时间内没有代码提交,这次构建反而是浪费。另一种方式是让 Jenkins 定时轮询,看看版本库中是否有代码提交。这种方法的缺点是当有了代码提交以后,Jenkins 要等到下一个轮询周期才能执行构建。当然,你也可以让 Jenkins 每分钟都轮询一次,尽可能缩短等待时间,但我们还有另一种更优雅的方案──给 Mercurial 版本库中放一个 post-commit 的钩子,这样一旦版本库接受了新代码,它都会通知 Jenkins,让它立刻开始构建。
这个方法需要在.hg 目录下的配置文件中(.hgrc)添加一个钩子。在这个钩子里面,你需要让 Mercurial 访问下面这个网址: http://localhost:8080/job/JOBNAME/build?delay=0sec(JOBNAME 需要替换成真实的 job 名称),让 Jenkins 启动构建。但不幸的是,Windows 下没有类似 Linux wget 这样可以进行 HTTP 请求的工具。我自己写了行 Ruby 代码来做这项工作。你也可以用 Powershell 来创建一个 System.Net.WebClient 的实例,然后调用 DownloadString 方法。读者朋友可以自己完成这项工作,权当练习吧。
如果你不愿意用这个方法,就还用轮询吧。回到 job 配置页面,在“Build Trigger”区域选择“Poll SCM”,在“Schedule”输入框中输入轮询周期。它采用的语法格式是 cron 的风格。如果要每分钟轮询一次,就输入“1 * * * *”。你可以点击输入框右侧的帮助按钮,查看轮询周期的语法介绍。
构建流水线
Jenkins 可以在某个构建成功结束之后启动其他 job。于是就有了构建流水线,它的概念就是一个 job 成功之后触发其他 job。触发者叫做上游 job,被触发者被称作下游 job。
构建流水线的应用场景有很多:让耗时较长的测试在单元测试结束之后执行;运行静态代码检查;把构建结果部署到试机环境(staging)或者产品环境中。我们下面来演示一下这个功能,让 Jenkins 在构建结束后启动 web 服务器,运行 HelloCI 这款应用。
我们只需要做三件事情:HelloCI-RunUnitTests job 成功之后触发一个新 job;把 HelloCI-RunUnitTests 的构建结果拷贝出来;启动 web 服务器。在开始之前,你还需要安装 Copy Artifacts 插件。回到 Manage Plugins 页面,参考之前安装 Mercurial 插件的方法安装 Copy Artifacts。在看到重启的提示信息时重启 Jenkins。
创建新 job 之前,我们需要告诉 HelloCI-RunUnitTests job,让它把构建产物保存下来,以供新 job 使用。回到 HelloCI-RunUnitTests job 的配置界面,找到“Post-build Action”,选中“Archive the artifacts”,页面上就会出现一个文本框:“Files to archive”。我们要存档的目录有两份,一份是 BuildArtifacts 目录,一份是 packages 目录,后者是为了让我们能够访问 NuGet package。如果要指定某个目录以及目录下所有内容,就需要在目录后面跟一个斜杠和两个星号。不同的目录或文件之间用逗号分割。在这里我们输入的是“BuildArtifacts/,packages/”。
现在创建一个新 job,起名叫“HelloCI-StartWebServer”。在配置页面上“Build Triggers”那一节里选中“Build after other projects are built”,把之前那个 job 配置成要触发当前 job 的项目。
接下来给 job 添加一个 build step,让这个 job 所做的第一件事情就是拷贝之前 job 所保存的构建产物。这里要用到“Copy artifacts from another project”这个 step(它是由 Copy Artifacts 插件提供的)。我们只需要填入 HelloCI-RunUnitTests job 的名字就可以。
最后还要添加一个 build step,让它启动 web server。这里用的是“Execute Windows batch command”,我们用 Visual Studio 提供的 Cassini 来运行应用。Cassini 可以在“C:\Program Files (x86)\Common Files\microsoft shared\DevServer\10.0\WebDev.WebServer40.EXE”找到。运行 Cassini 很简单,把 web 应用存放的路径作为参数传给它就行,如果没有指定端口号的话,它就会使用默认的端口号 80。
“Execute Windows batch command”step 需要一个可以在 BuildArtifacts 和 packages 拷贝后的目录下执行的命令。我们把下面这个命令复制到“Command”输入框中:
"C:\Program Files (x86)\Common Files\microsoft shared\DevServer\10.0\WebDev.WebServer40. EXE" /port:9876 /path BuildArtifacts/_PublishedWebsites/HelloCI.Web/.
这个命令可以启动 Cassini,把它指向拷贝过来的应用,然后在 9876 端口启动服务器。
所有的配置都已就绪。试着修改一下代码然后提交。Jenkins 应该能够监听到变化,运行 HelloCI-RunUnitTests。如果代码编译成功,所有测试均以通过,Jenkins 应该开始运行 HelloCI-StartWebServer job,把 HelloCI-RunUnitTests 的构建产物拷贝过来,在 9876 端口启动 Cassini。如果这一切都运行无误,我们的持续集成系统就搭好了。
结尾
MSBuild 和 Jenkins 可讲的地方还很多。比如说可以从持续集成走向持续部署。你所需要的只是一个 job,让它把构建产物部署到生产环境(译者注:持续部署殊非易事,作者有些夸大其词,读者请勿轻信)。ASP.NET 的应用就更简单了,用一个 job 把构建产物复制到某个目录下就行。这样一来,只要所有的测试全都通过,这个 Jenkins job 就可以把最新特性发布到生产环境上去。
如果你还没做好准备走那么远,也可以用一下 Promoted Build plugin,它可以在某个构建达到了一个特定的标准之后,把它标记为“推荐使用”。一个人或几个人手工审核过一个构建,也算得是达到了推荐标准。有了 Promoted Build plugin,人们做出的修改就可以自动发布到开发服务器上,所有的新功能都集中呈现。开发经理而后可以把某个特定的构建推荐到试机环境中。QA 团队或其他干系人可以进行验收(其方式就是推荐试机环境上的构建),然后这个构建就可以由 Jenkins 部署到产品环境上。
John Ferguson Smart 曾写过一本很出色的书,叫做“Jenkins: The Definitive Guide”。这本书除了讲述 Jenkins 的功能之外,还描述了怎么保证 Jenkins 的安全,怎样检测资源使用(如磁盘占用),怎样进行分布式构建,直到设置通知系统,让用户在构建开始、成功、失败的时候得到通知。这本书由 O’Reilly 出版,购买地址在此。如果你对Jenkins 还不太了解的话,本书就属于必看的一本。
拥有一个自动化的构建部署系统,就跟要看重测试,要使用版本控制系统一样重要。我希望你已经熟练掌握了如何设置持续集成系统,可以开始着手集成自己的系统。
一开始不要太激进,一心想着把所有事情都做好。你可以一点一点来。你终会享受到胜利果实的。当Jenkins 通知你某个构建失败的时候,你发现bug 的时机就提前了;当你再也不用记忆每一步部署的操作时,你就有了一整套系统化的部署流程。而更重要的是,你有了更多时间去做更重要的事情,比如开发有价值的特性,或是为新项目做准备,也可以提前下班回家,再也不用为即将到来的上线担忧。
关于作者
Mustafa Saeed Haji Ali居住在 Somaliland 的 Hargeisa。他是个程序员,最常用的是 ASP.NET MVC。Mustafa 喜欢测试,喜欢用 Javascript 框架,如 KnockoutJS、AngularJS、SignalR。他热衷于传播最佳实践。
查看英文原文: Continuous Integration with MSBuild and Jenkins – Part 2
评论