关于本教程
本教程讨论持续集成的基本问题:什么是持续集成,为什么需要它,它是如何工作的,以及
CI 环境中的开发步骤。本教程讲解如何设置 CI 过程来建立一个可重复的可靠的构建过程。
您将学习如何正确地配置 CI 服务器,让它查询 SCM 存储库,并在探测到源代码中的修改时运行
Ant 构建过程。还要学习如何运行自动的 JUnit 测试,以及如何用 PMD 和 FindBugs 进行软件检查。最后,体会一下
Hudson(一种出色的 CI 服务器)如何在问题发生时发出通知,最终帮助您更快速地构建可靠的软件。
目标
本教程使用 Hudson、Ant 和 Subversion 作为框架,讲解持续集成的基本概念。在学完这个一小时的教程时,您会理解持续集成的好处,以及如何正确地设置和配置
Hudson、Ant 和 Subversion。产生的构建过程将运行测试和软件检查,并在错误发生时尽快报告。
先决条件
为了从本教程获得最大的收益,您应该熟悉 Java? 开发。本教程还假设您理解构建具有适当质量的软件的价值,并熟悉
JUnit。
软件需求
持续集成环境需要一个自动构建工具、一个代码存储库和一个 CI 服务器。为了实践本教程中的代码,需要安装
Java 平台以及 Hudson 1.150、Ant 1.7、JUnit 3.8.1 和 Subversion
1.4.x。
对于本教程,推荐的系统配置如下:
- 一个支持 Sun JDK 1.5.0_09(或更高版本)或 IBM Developer Kit
for Java 1.5.0 SR3 的系统,至少 500 MB 主内存
- 安装软件组件和示例需要至少 20MB 的硬盘空间
教程中的说明基于 Microsoft? Windows? 操作系统。本教程中的所有工具也可以用在
Linux? 和 Unix? 系统上。
持续集成的核心概念
CI 过程会经常构建软件组件;在许多情况下,每当源代码存储库(比如 Subversion
或 ClearCase)中的代码发生变化时,都要构建软件组件。CI 的好处是:经常构建软件可以确保尽早遇到问题(比如代码缺陷),避免问题在软件开发周期晚期变复杂时才被发现。
工具与过程
尽管 CI 实际上是一个过程,但是持续集成 这个词常常与一个或多个工具相关联。在本教程中,讲解如何安装、配置和使用
Hudson 作为 CI 服务器,但是要记住,CI 远不只是个工具。实际上,使用的工具可能是 CI 比较次要的方面,因为
CI 工具所做的仅仅是在代码存储库中探测到修改时运行构建。构建过程本身比用来运行它的工具重要得多。
开始使用 CI
开始使用 CI 需要三个组件:
- 用 Ant 或 Maven 等工具建立的自动构建过程
- 一个代码存储库,比如 CVS 或 Subversion
- 一个 CI 服务器,比如 Hudson,但是 cron 作业也可以满足需要
我们来详细讨论这些组件。
自动的构建
CI 过程会经常集成软件,这需要通过构建来完成。在 Java 环境中,Ant
是常用的构建平台。可以使用 Ant 可靠地自动执行编译、测试等任务,甚至可以执行软件检查和部署。在掌握了
CI 的所有组件之后,您会发现构建策略是成功的 CI 过程最重要的方面。如果缺少适当的构建过程,CI 就难以发挥作用。
源代码管理
为了让 CI 正确地发挥作用,需要一个源代码管理(SCM)系统或存储库,比如
Subversion 或 CVS。CI 服务器向 SCM 存储库查询代码修改。在找到修改时,CI 服务器执行签出(即更新本地沙箱)并执行构建。除了执行得更频繁之外,构建过程与在本地环境中执行的构建相同。
CI 服务器
对于成功的 CI 过程,需要用一个自动的过程监视 SCM 存储库并在探测到修改时运行构建,这也非常重要。对于
Java 平台,有许多可用的 CI 服务器,包括开放源码软件和商业产品。它们的基本配置都很相似,适合监视特定的
SCM 并在探测到修改时运行构建。所有 CI 服务器都有自己的优缺点。Hudson 尤其让人感兴趣,因为它容易配置而且具有强大的插件,这些插件可以显示测试结果趋势等信息。
Hudson 简介
Hudson 是一种革命性的开放源码 CI 服务器,它从以前的 CI
服务器吸取了许多经验教训。Hudson 最吸引人的特性之一是它很容易配置:很难找到更容易设置的 CI 服务器,也很难找到开箱即用特性如此丰富的
CI 服务器。Hudson 容易使用的第二个原因是它具有强大的插件框架,所以很容易添加特性。例如,一个
Hudson 插件可以随时间的推移跟踪 FindBugs 和代码覆盖。它还可以报告测试结果的趋势(来自
JUnit 或 TestNG)以及构建结果和对应的执行时间。
Hudson 需要运行 Java 5。如果需要使用 Hudson 附带的嵌入式容器(Winstone)之外的其他容器,那么只需使用一种
Servlet 2.4 容器。对于大多数情况,Winstone 就足够了。
开始使用 CI
本节讨论如何把 CI 过程的各个组件组合在一起,以及采用这种方式的原因。在此之后,就可以开始编写代码了!
先决条件:一个构建系统!
可重复的可靠构建是可预测的软件过程的基础。正如前面提到的,Ant 是
Java 平台上流行的构建工具,它的主要用途是自动执行编译和部署等常见任务。Ant 还有助于用 JUnit
和 TestNG 等测试框架进行单元测试,而且它可以与许多其他工具集成,比如 PMD 和 FindBugs(用来自动执行静态代码分析)。使用
Ant 编译源文件很容易,只需在命令提示下发出ant compile 命令。
可靠性与可重复性
尽管 Ant 有助于保证可重复性,但是可重复性 主要取决于开发人员自己。在软件构建方面,可重复性意味着,不同的开发人员在发出编译或测试等命令时,会得到相同的结果。如果由于某种原因(比如构建本身没有显式引用一个必需的库),一位开发人员无法完成编译,而另一个开发人员可以完成编译,那么构建就是不可靠的,也可以认为它是不可重复的!
可靠性和可重复性对于 CI 非常重要。因为 CI 是一个没有人为干预的自动过程,所以
CI 最终调用的构建过程必须没有瑕疵。构建过程必须是一个简单的活动,不依赖于 CI 服务器无法控制的东西,比如需要精确引用的库、没有明确文档记录的位置和手工刷新的数据库。当然,某些构建操作会失败,例如某人签入了无法编译的代码;但是构建不能由于本身的缺陷而失败。
基本的构建过程
要想让 CI 能够促进软件开发过程,构建的作用必须不仅仅是编译代码。因为在每次修改代码时都会运行构建,所以这是运行测试和代码检查的好时机。
基本的构建过程包含以下任务:
- 编译源代码,包括测试
- 执行测试,包括用 JUnit 或 TestNG 编写的测试
- 运行代码检查,比如 PMD
- 将最终的产品存档为 JAR、WAR 或一系列文件
- 部署最终的产品(假设最终产品允许部署)
请记住,这些是最基本的步骤,构建过程可能包含更高级的步骤,比如处理数据库或软件产品的其他方面。
设置目录结构
有许多种设置软件项目目录结构的策略,它们各有优缺点。我喜欢把源代码 与测试
分隔开的方式,而且我总是为它们创建不同的目录结构。处理第三方库的常用方式是创建一个 lib 目录。但是,对于处理第三方依赖项,还有可管理性更好的机制,比如
Ivy。
无论怎样设置项目的目录结构,目录结构越详细、组织性越强,效果就越好。随着项目资产(比如源代码文件和测试)数量的增加,目录结构的好处会越来越明显。图
1 给出一个简单的项目目录结构,其中包含一个用于第三方库的目录,以及两个用于源代码文件和相关测试的目录:
图 1. 一个简单的项目目录结构
注意项目根目录中的 build.xml 文件(见图 1)。这是项目的构建文件,它定义了一组最基本的自动操作,可以将源代码转移到可投入生产环境的状态。
CI 基础:代码编译
现在,可以开始编写代码了!本节为软件项目设置基础结构,包括设置项目类路径和编译。如果不执行这些预备步骤,后面的工作就不会有效果。
用 Ant 执行编译
创建可靠且可重复的构建的第一步是限制硬编码的值,尤其是与文件系统路径相关的值,比如目录。因此,清单
1 定义了许多属性,以后可以在各种相关目标中引用它们:
清单 1. 在 Ant 中设置属性
<property name="default.target.dir" value="target" />
<property name="classes.dir" value="${default.target.dir}/classes" />
<property name="test.classes.dir" value="${default.target.dir}/test-classes" />
<property name="test.report.dir" value="${default.target.dir}/test-reports" />
<property name="lib.dir" value="${basedir}/lib" />
<property name="source.dir" value="src/java" />
<property name="test.source.dir" value="test/java" />
<property name="test.pattern" value="**/**Test.java" /> |
创建类路径
因为所有第三方库都放在 lib 目录中,所以可以通过扫描这个目录快速创建类路径,见清单
2。(注意,通过清单 1 中的 lib.dir 变量引用这个目录。)
清单 2. 根据 lib 目录中的 JAR 文件创建类路径
<target name="init">
<mkdir dir="${classes.dir}" />
<mkdir dir="${test.classes.dir}" />
<path id="build.classpath">
<fileset dir="${lib.dir}">
<include name="**/*.jar" />
</fileset>
</path>
</target> |
编译源代码
定义了类路径之后,就可以创建一个编译源代码的目标,见清单 3。
清单 3. 使用 Ant 的 javac 任务编译源代码
<target name="compile-source" depends="init"
description="compiles all .java files in source directory ">
<javac destdir="${classes.dir}" srcdir="${source.dir}" classpathref="build.classpath" />
</target> |
很容易通过一个称为 javac 的任务定义编译。这个任务使用类路径编译一个目录中的代码,并将类文件放在另一个目录中。
测试、监视和存档
如果想让 CI 提供真正的价值,除了持续编译之外,还需要做更多的工作。如果想提高代码质量,首先就要执行测试。可以使用
JUnit 或 TestNG。具体选用哪种测试框架并不重要,重要的是要经常 运行这些测试,也就是每当修改代码时都运行测试。
用 JUnit 进行测试
幸运的是,用 Ant 运行 JUnit 测试非常容易:Ant 允许通过
junit 任务运行 JUnit 测试。在配置 junit 任务时,需要考虑寻找 JUnit 测试的位置以及如何获取这些测试的结果。
清单 4. 通过 Ant 运行 JUnit 测试
<target name="test" depends="compile-tests" description="runs JUnit tests">
<mkdir dir="${test.report.dir}" />
<junit dir="${basedir}" printSummary="on" fork="true" haltonfailure="true">
<sysproperty key="basedir" value="${basedir}" />
<formatter type="xml" />
<classpath>
<path refid="build.classpath" />
<pathelement path="${test.classes.dir}" />
<pathelement path="${classes.dir}" />
</classpath>
<batchtest todir="${test.report.dir}">
<fileset dir="${test.source.dir}">
<include name="${test.pattern}" />
</fileset>
</batchtest>
</junit>
</target> |
在清单 4 中,junit 任务运行在 test.source.dir(在
清单 1 中定义)目录中找到的所有测试。XML 报告写到另一个目录(test.report.dir)中。在开始配置
CI 服务器时,要使用这些测试报告写到的位置。
软件检查
有了 Ant 这样的自动化机制,就可以以多种方法监视软件质量。有许多种扫描源代码和二进制文件的工具,但是其中最流行的两种是
PMD 和 FindBugs。PMD 扫描源代码文件,并根据一系列规则检查其中的代码。如果代码有问题,它就会报告一个违规。FindBugs
的作用与 PMD 相似,但是它扫描二进制文件(即类文件)并报告 bug。这两个工具都能够很好地与 Hudson
集成。
运行 PMD
通过 Ant 运行 PMD 很容易:只需 下载它并按照说明操作!与运行
JUnit 测试一样,一定要指定在运行 PMD 时生成 XML 报告
清单 5. 用 PMD 寻找代码违规
<target name="pmd" depends="init">
<taskdef name="pmd" classname="net.sourceforge.pmd.ant.PMDTask"
classpathref="build.classpath" />
<pmd>
<ruleset>rulesets/basic.xml</ruleset>
<ruleset>rulesets/braces.xml</ruleset>
<ruleset>rulesets/javabeans.xml</ruleset>
<ruleset>rulesets/unusedcode.xml</ruleset>
<ruleset>rulesets/strings.xml</ruleset>
<ruleset>rulesets/design.xml</ruleset>
<ruleset>rulesets/coupling.xml</ruleset>
<ruleset>rulesets/codesize.xml</ruleset>
<ruleset>rulesets/imports.xml</ruleset>
<ruleset>rulesets/naming.xml</ruleset>
<formatter type="xml" toFile="${default.target.dir}/pmd_report.xml" />
<fileset dir="${source.dir}">
<include name="**/*.java" />
</fileset>
</pmd>
</target> |
PMD 有许多规则,可以对自己的代码库运行这些规则。清单 5 给出许多选项,但是选择运行哪些规则完全取决于您。
运行 FindBugs
FindBugs 扫描二进制文件,而且只扫描包含项目文件的 JAR 文件常常更容易。清单
6 中的 findbugs 目标依赖于 jar 目标:
清单 6. 用 FindBugs 寻找 bug
<target name="findbugs" depends="jar">
<taskdef name="findbugs" classname="edu.umd.cs.findbugs.anttask.FindBugsTask"
classpathref="build.classpath" />
<findbugs classpathref="build.classpath" pluginlist="${lib.dir}/coreplugin-1.0.jar"
output="xml" outputFile="${default.target.dir}/findbugs.xml">
<sourcePath path="${source.dir}" />
<class location="${default.target.dir}/plainead.jar" />
</findbugs>
</target> |
与 JUnit 和 PMD 任务一样,应该让 FindBugs 创建 XML
文件。
将二进制文件存档进 JAR
无论最终如何部署项目,都需要选择创建 WAR 或 JAR 文件。对于这个项目,我们创建一个
JAR 文件。如清单 7 所示,从一系列类文件创建 JAR 文件也很容易:
清单 7. 用 Ant 对二进制文件进行存档
<target name="jar" depends="test">
<jar jarfile="${default.target.dir}/plainead.jar" basedir="${classes.dir}" />
</target> |
在本节和前一节中,我们只创建了几个目标,而最终结果是一个可重复且可靠的构建系统,它几乎适合任何代码库。这个构建文件不但执行编译,而且还运行测试。它还使用两种检查工具评估代码质量。最后,这个构建文件将代码库打包成
JAR 文件。请记住,最后一步因项目而异。如果要构建 Web 应用程序,那么可能需要比清单 7 更多的控制;例如,可能需要用应用程序的文件创建一个
WAR 文件,然后把它部署到 Web 容器中。
没有 SCM,就没有 CI
有了可靠的构建过程之后,CI 过程的下一个需求是一个 SCM 存储库。市场上有许多
SCM 存储库,包括开放源码软件和商业产品。这些产品的基本用途都是管理源代码。SCM 系统可以进行源代码版本和历史管理,当多个开发人员在同一个文件上工作时,这两个功能十分重要。如果当前的软件项目没有使用
SCM 存储库,那么您可能应该停止阅读本教程,尽快设置并运行一个 SCM 系统。
SCM 与 Hudson 的集成
Hudson 默认支持 CVS 和 Subversion,这两个软件都是免费的。Hudson
还有用来集成 Rational ClearCase(这是一个商业产品)的插件。
使用 Subversion
对于本教程,假设您使用 Subversion。如果不使用 Subversion,也不必担心:无论使用哪种
SCM 系统,基本原理都是一样的。实际上,在高层面上,CI 过程与 SCM 相关的方面是非常简单的。CI
服务器(在本教程中是 Hudson)向 SCM 查询对特定项目的修改。如果探测到修改,CI 服务器就执行更新(也就是获取
SCM 中的最新副本)并运行构建。在这个示例中,Hudson 运行本教程前面定义的构建。
基于 URL 的访问
按照 Subversion 的行话来说,项目驻留在存储库中。根据存储库的配置方式,可以通过一个
URL 访问项目,URL 实际上是存储库的路径加上项目名称。如果使用别的 SCM 系统,那么可能要使用其他机制访问存储库。无论是哪种情况,都需要正确地配置
CI 服务器来访问项目存储库。目前,假设您使用 Subversion,所以只需通过 URL 签出所需的项目。
假设项目名称是 solar-ci,存储库的 URL 是:
http://scm.acme.com/svn-repo/main/trunks/
所以,项目的访问 URL 是:
http://scm.acme.com/svn-repo/main/trunks/solar-ci
必须根据 SCM 的设置方式正确地配置存储库访问;例如,Subversion
需要用户名和密码。在设置 CI 过程时,为 CI 服务器创建一个新用户常常是有意义的。对于这个项目,我们创建一个名为buildmaster
的新用户。在后面配置 Hudson 来监视项目时,为了执行签出和其他功能,需要显式地指定buildmaster
的凭证。
Hudson
CI 过程的最后一个方面是 CI 服务器本身。CI 服务器在整个开发过程中的主要作用是控制者:当服务器在代码存储库中探测到修改时,它将运行构建的任务委托给构建过程本身。如果构建失败了,那么
CI 服务器将通知相关方面,然后继续监视存储库。它的角色看起来是被动的;但是,它是快速反映问题的关键。
安装 Hudson
使用 Hudson 的主要好处之一是它的设置很简单。在最简单的情况下,Hudson
只需要两个步骤:
1.下载最新的版本(它打包为一个 WAR 文件)。
2.运行 java -jar hudson.war。
这样就可以了。因为下载的是一个 WAR 文件,所以如果愿意,可以将它部署在
Tomcat 或 JBoss 等容器中。这完全由您自己决定。当然,Hudson 假设在安装它的机器上运行着
Java 5,而且如果定义了JAVA_HOME 环境变量,Hudson 就会使用它。(正如前面提到的,Hudson
需要 Java 5。)
在安装并运行 Hudson 之后(将 WAR 文件部署到 servlet
容器或从命令行执行 java -jar hudson.war),启动浏览器并访问默认安装位置。如果通过命令行运行
Hudson 而且您在本地机器上,那么可以访问http://localhost:8080/。
图 2. Hudson 已经就绪!
如果一切正常(实际上不太可能出问题),应该会看到图 2 所示的 Hudson
启动页面。
配置 Hudson
配置 Hudson 的第一步是让它知道在哪里可以找到构建平台的可执行文件。在这个示例中,用
Ant 作为构建系统,所以需要告诉 Hudson 本地机器上 Ant 的位置。如果使用 Maven,也必须做同样的工作:告诉
Hudson Maven 的位置。请记住,并不是告诉 Hudson 构建文件的位置,而是指定构建系统的可执行文件的位置,让它可以调用
构建文件。在这个示例中,需要指定 Ant 命令的位置,这让 Hudson 能够执行ant -f build.xml命令。
如果访问 Hudson 主页的本地实例并单击左上角的 Manage Hudson
链接,应该会看到图 3 所示的可配置选项列表。
图 3. 配置 Hudson 非常容易
在 Ant 部分中,需要提供安装 Ant 的路径,见图 4:
图 4. 向 Hudson 指出 Ant 的位置
还可以配置服务器的其他几个方面,比如向 Hudson 提供一个电子邮件服务器的位置,以便在构建失败时接收电子邮件。根据您的组织设置电子邮件的方式,可能需要让系统管理员帮助设置这个特性。设置电子邮件并不是必需的;Hudson
还支持以 RSS 作为通知机制,对于某些人来说,这种方式比电子邮件更好。究竟选择哪些通知机制完全取决于您。(注意,这里说的是
“哪些”,也就是说,可以同时使用多种通知机制!)
最后,在配置项目之前,需要让 Hudson 能够与您的 SCM 系统通信。在这个示例中,需要设置
Subversion 存储库的路径和访问存储库所需的凭证。
在浏览器中访问 http://localhost:8080/scm/SubversionSCM/enterCredential,并指定项目的存储库
URL 以及正确的凭证(比如 buildmaster 等等)。这个一次性的步骤确保 Hudson 可以正确地与
Subversion 通信。(当然,如果决定改用另一个 URL 上的另一个 Subversion 存储库,就必须重新设置。)
在 Hudson 中配置项目
既然 Hudson 已经能够与 SCM 存储库通信了,就该配置项目了。这个示例所用的项目称为
solar-ci。在 Hudson 主页上单击左上角的New Job 链接。这时会看到图 5 所示的屏幕:
图 5. 在 Hudson 中配置作业
给作业命名(对于这个示例是 Solar Project)并选择 Build
a free-style software project 选项。如果使用 Maven 2,那么 Hudson
可以根据项目的配置文件快速配置项目。
项目细节
单击 OK 之后,会进入项目配置屏幕,在这里可以指定以下内容:
- 要连接的 SCM
- 构建项目的频率
- 要调用的构建平台(Ant、Maven 等等)
另外,还可以配置一些构建后操作,比如发送电子邮件或发布相关软件资产。图
6 所示选项的意义很明确,不需要解释。在 Hudson 中设置 CI 项目就是这么容易。
图 6. 在 Hudson 中配置项目细节
对于这个项目,需要选择 Subversion 选项,然后至少需要指定项目的
URL。
安排检查
在 Build Triggers 部分中有许多选项。我发现 Poll
SCM 选项非常有用,它决定 Hudson 检查 SCM 的频率。这个设置取决于您的需要;如果开发团队很大,对代码的修改很频繁,就需要比较频繁地检查
SCM(比如每 5 分钟一次)。
所以,选择 Poll SCM 选项,然后在 Schedule 框中输入
* * * * *,这让 Hudson 每分钟检查一次。这样设置有助于进行演示(在修改 SCM 中的代码之后,不用等很久就会触发构建过程),但是在对
CI 过程满意之后,要记住指定更合理的值。单击问号(?)图标,可以了解关于配置 cron tab 的更多信息。
配置 JUnit 测试趋势
在 Build 部分中,选择 Invoke Ant 选项,选择前面配置的
Ant 版本,然后指定构建文件中要执行的目标。目前,只需让 Hudson 执行测试目标。这个目标将编译所有源代码文件,然后运行已经定义的所有
JUnit 测试。
在 Post-Build Actions 部分中,选择 Publish
JUnit test result report 选项。必须指定在通过 Ant 运行 JUnit 时生成
XML 文件的位置。如果 Subversion 中的项目名称是solar-ci,而且构建文件把这些报告直接写到target/test-reports
目录中,那么应该输入solar-ci/target/test-reports/*.xml,见图 7:
图 7. 在 Hudson 中配置 JUnit 趋势
单击 Save 保存配置。
项目主页
如图 8 所示,Hudson 显示一个包含许多选项的项目主页,可以在这里修改配置、强制执行构建、查看与项目资产相关的修改等等!下一节讨论如何使用这些选项更深入地观察软件项目的所有方面。
图 8. Hudson 中的 CI 项目主页
准备好执行 CI 了吗?
现在已经在 Hudson 中正确地配置了 CI 项目,所以差不多 可以开始运行它了!但是在此之前,我们来检查一下项目的设置是否正确。
检查构建的状态
我们已经将 Hudson 配置为每分钟检查一次,而这个项目还没有构建过,所以
Hudson 很快就会自动触发构建过程。检查项目的主页,在页面左下部分的 Build History 框中会看到一个新条目,见图
9:
图 9. Hudson 运行了一个构建!
更详细的信息
单击这个构建的日期,就会显示这个构建的详细信息,见图 10:
图 10. Hudson 的 Build Status 页面
在图 10 所示的 Build Status 页面上,可以查看对源代码的修改情况(这个初始构建没有报告修改)以及测试结果。另外,还可以通过单击
Console Output 链接查看构建过程的输出。
还可以做什么?
如果返回项目的主页,就会注意到可以通过 RSS 订阅项目的 Build
Statu 页面。还要注意,可以订阅所有构建的报告,也可以只订阅失败时的报告。我常常选择订阅failures,因为在构建失败时我需要得到警告。
除了检查构建状态之外,Hudson 还允许强制执行构建(单击 Build
Now 链接)、查看对多个构建的修改以及查看 Hudson 执行构建的工作空间。(如果需要获取项目资产,例如
WAR 文件,可以在工作空间中进行操作。)
编写一些代码!
CI 过程需要三个组件:构建、SCM 和 CI 服务器。现在,这些都设置好了,可以开始执行
CI 了。当然,为了让 CI 发挥作用,需要编写一些代码并进行修改。在本节中,我们从头到尾看看 CI 过程是如何工作的。
CI 的节奏
在研究完整的 CI 场景之前,先谈谈使用 CI 时的开发节奏。使用 CI
服务器运行构建并不妨碍您亲自运行本地构建。实际上,在正确的 CI 过程就位之后,它会将 SCM 中的失败通知您,所以要尽可能确保新代码或修改的代码工作正常,然后才能将它们签入
SCM 存储库。这样做可以避免大量警告不必要地干扰所有开发人员。
因此,CI 环境中的开发节奏应该是下面这样:
1.对本地沙箱执行更新。
2.修改一些代码。
3.编写一些测试。
4.运行本地构建,从而确保您没有破坏任何东西。
5.执行更新,从而确保从 SCM 获得最新的代码;然后再次运行构建,确保一切正常。
6.签入您的修改。
注意,如果您和团队的其他成员同时在代码库中工作,那么第 5 步是非常重要的,尤其是在您的工作花费了很长时间的情况下。
大多数开发人员会发现 CI 的节奏是很自然的。无论是否在 CI 环境中,这都是良好的工作节奏!
不断增加的测试
现在,可以开始做一些真正的工作。例如,当 Hudson 运行示例项目的第一次构建时,它发现并运行两个
JUnit 测试;因此它报告这两个测试都通过了,它们都是新的测试,见图 11:
图 11. 这些测试顺利通过了
您可能为这些测试顺利通过感到高兴,也可能不以为然,认为它们没有提供足够的覆盖范围。(当然,可以获取覆盖报告,但是目前不必这么做。)为了确保代码是完善的,编写另外两个测试,在本地运行它们,执行更新,再次运行构建,然后签入它们。在此之后,编写另外两个测试
并重新执行相同的步骤。
检查构建状态
在签入两个新测试之后,可以访问项目的主页,检查它们的状态。过一会儿,在
Build History 框中会出现一个尚未完成的构建。Hudson 已经在 SCM 存储库中探测到了修改!
图 12. Hudson 对 Subversion 中的修改做出反应
检查构建的细节
在图 12 中,Hudson 的 Build History 框显示一个尚未完成的构建。当这个构建完成之后(应该会顺利地完成),可以单击这个构建的日期来查看详细信息,见图
13:
图 13. 运行两个新测试之后的详细信息
图 13 显示触发这个构建的修改。可以单击每个修改旁边的细节链接来查看关于修改的更多信息,比如谁做了这个修改和它们的注释语句,以及图
14 所示的特定细节:
图 14. 在这个构建中发现两处修订
测试通过了吗?
如果返回构建状态页面并单击 Test Result 链接,那么可以看到运行了两个新测试,所有四个测试都通过了,见图
15:
图 15. 测试报告
测试趋势
在配置这个项目时,我们指定了 JUnit 的结果文件,但是我没有说明为什么这么做。这么做是为了演示
Hudson 支持的一个出色的开箱即用特性。对于每个构建,Hudson 会解析对应的 JUnit 结果
XML 文件并生成趋势图。实际上,如果返回项目的主页,就会看到一个趋势图,它显示对于到目前为止的两个构建,测试数量已经加倍。
图 16. 一个不错的趋势,测试数量已经加倍
图 16 中的趋势图表明,在 Builds 6 和 8(在这里,它们是连续的构建)之间测试数量已经加倍。
增加两个测试之后的趋势图
如果再编写两个测试,那么图 16 中的趋势图会继续显示非常不错的趋势。也就是说,随着每个构建的运行,测试的数量逐渐增加,见图
17。还可以看出这些测试一直顺利通过,因为趋势图是蓝色的,而不是红色:
图 17. 测试趋势持续上升!
检查时间
Hudson 不但生成测试的趋势,还生成构建执行时间的趋势,所以很容易观察构建的性能。例如,如果构建的执行时间开始变长,那么可能要考虑以不同的方式运行测试(比如对测试进行分类),从而加快构建过程。
图 18. 这些测试要花时间运行!
图 18 所示的数据清楚地表明,测试增加了构建的总执行时间;编译这些测试要花费时间,但是与运行它们的时间相比,编译时间非常短。
报告卡:情况良好
如果返回 Hudson 的主页,就会看到 Hudson 当前管理的项目列表。在项目表中可以找到各种数据。其中比较有意思的是
W 列中的第二个图标:它表示监视的项目的总体健康程度。如图 19 所示,这个图标目前是发光的太阳,这表示对于所有构建,测试都通过了:
图 19. 阳光明媚的好日子
另一个报告卡:情况糟糕
与真实生活中一样,不可能每天都是阳光明媚的;当出现问题时,例如项目无法构建或测试失败了,Hudson
会显示另一个图标,见图 20:
图 20. 乌云密布
如果仔细观察,就会看到一个弹出框,它指出某个项目的构建稳定性是 79%,测试稳定性是
97%,这相当于多云的天气。但是,最上面的项目非常糟糕。
Hudson 的插件
在这个项目开始时定义的构建文件运行软件检查器 FindBugs 和 PMD,分析代码并报告违规。将这些任务添加到
CI 过程中,就可以很好地监视代码库。常常运行它们,就可以观察结果的趋势,就像前面看到的测试和构建时间趋势一样。
Hudson 比较令人感兴趣的特性之一是它的插件 API,这个 API
有助于创建新特性并根据需要安装。Hudson 支持许多插件,包括用于生成 FindBugs 数据趋势的插件和生成
PMD 违规趋势的插件(这个插件也可以为其他工具生成趋势,包括 CheckStyle)。
就像安装 Hudson 本身一样,安装 Hudson 插件也是很容易的。只需要从
Hudson 的 Web 站点下载插件的最新版本,单击 Hudson 主页上的Manage Hudson
链接,然后单击 Manage Plugins 链接,这时会显示一个用来上传插件存档文件的表单。上传插件之后,必须重新启动
Hudson。
FindBugs
下载并安装 FindBugs Hudson 插件之后,就需要配置它。插件的配置在项目级进行,所以要访问项目的主页并单击
Configure 链接,然后会看到与 FindBugs 相关的一些新选项,见图 22:
图 22. 在 Hudson 中配置 FindBugs
配置 FindBugs 插件的步骤与配置 JUnit 趋势很相似。必须指定
FindBugs 输出 XML 报告的位置(在定义构建文件中的 FindBugs 目标时使用过这个设置)。除了报告的位置之外,还可以指定一个阈值。如图
22 所示,指定阈值为 5 表示如果有 5 个以上的 FindBugs 违规,就认为这个构建是不可靠的。还可以用阈值影响构建报告。
单击 Save 之后,需要更新项目的构建首选项,这意味着需要确保 Hudson
在构建过程中运行 FindBugs。在此之后,需要强制执行构建(否则只能等待有人签入修改),从而开始收集
FindBugs 数据。
图 23. 发现两个警告
使用 FindBugs
执行第一个构建之后,在构建状态页面上会出现一些新的数据点,见图 23。在这个示例中,Hudson
报告在运行 FindBugs 时发现了两个警告。通过单击 Hudson 报告页面上的FindBugs 链接,可以查看更多的信息。
图 24. FindBugs 违规细节
查看细节有助于纠正问题,见图 24。在这个示例中,可以看到项目的两个类出现了违规。一个是
Priority 1 违规,另一个是 Priority 2 违规。根据 CI 的开发节奏,应该马上开始纠正这些问题。
纠正错误并将修改签入 Subversion 之后,Hudson 最终会执行构建。在执行构建之后单击
Build Status 链接,会看到 Hudson 指出一个 bug 已经纠正了,见图 25:
图 25. 一个 bug 被纠正了,另一个仍然存在
另外,项目主页上会显示新的趋势图(见图 26),它表明已经纠正了一个
bug:
图 26. bug 数量呈下降趋势 —— 不错!
显示 FindBugs 违规的趋势和使用 bug 阈值影响构建的总稳定性是两个出色的
Hudson 特性。而且,设置这两个特性非常容易。只需下载并安装 FindBugs,就可以在项目构建过程中运行
FindBugs。
PMD
还有一些 Hudson 插件与代码检查相关,其中最著名的是 Violations
插件,它可以为多种工具生成趋势,包括 PMD、CheckStyle 和 FindBugs 等。既然已经运行了
FindBugs 插件,就可以用 Violations 插件专门监视 PMD 数据。在本教程前面设置 Ant
构建文件时,我们让 PMD 输出 XML 报告。Violations 插件使用这些报告分析数据趋势。
如图 27 所示,这个插件的设置方式与 Hudson 中的其他趋势工具相似:只需指定要监视的工具的
XML 报告位置。当然还有其他选项,但是对于基本特性,指定 XML 报告的位置并单击Save 就够了。
图 27. 配置 Violations 插件
请记住,需要让 Hudson 实际调用运行这些工具的 Ant 目标;在这个示例中,已经定义了一个
PMD 目标,所以只需更新 Hudson 来运行 PMD(以及 FindBugs 和测试,运行测试会导致编译)。
修复并重复
在执行构建并研究 PMD 数据之后,可以开始纠正代码。如图 28 所示,在
Builds 13 和 14 之间减少了 13 个 PMD 违规。不错吧?代码已经越来越好了!
图 28. 已经纠正了一些 PMD 违规!
趋势
Violations 插件也支持趋势;如图 29 所示,它甚至按照工具分别生成数据趋势;在这个示例中,它同时寻找
FindBugs 数据和 PMD 数据。随便问一句,您能够从图 29 中看出一些 FindBugs 违规也已经被纠正了吗?
图 29. 违规呈下降趋势
与前面看到的测试趋势和 FindBugs 趋势一样,Violations
插件也在项目的主页上显示总违规数量的变动情况,见图 30:
图 30. 违规数量随时间变化的趋势
显然,Hudson 的插件让我们能够一目了然地看到代码基的情况;而且,显示数据趋势的特性让我们能够设置目标并监视它们的变化,这样就不必反复查看定制报告的具体内容。从某种意义上说,Hudson
创建了一个项目 “仪表板”,让我们能够快速理解数据。
结束语
持续集成过程要经常编译、测试、检查和部署源代码。CI 的好处很容易理解:经常组装软件,就可以在早期发现问题,而问题在早期还不复杂,容易解决。
在本教程中,您了解了 CI 的基本方面。我介绍了一些用于 CI 的工具(即
Hudson、Ant 和 Subversion),并讲解了如何使用这些工具设置 CI 环境。本教程使用的框架提供一个可重复且可靠的构建过程,这个构建过程运行
JUnit 测试并通过 PMD 和 FindBugs 进行软件检查。学习了如何正确地配置 Hudson
CI 服务器,让它检查 SCM 存储库并在探测到修改时运行构建过程。还学习了如何使用 Hudson 的插件生成各种结果的趋势。
现在您应该有信心快速地构建高质量的软件了! |