持續集成(Continuous Integration,CI)是持續地編譯、測試、檢查和部署 源代碼的過程。在許多持續集成環境中,這意味著每當源代碼管理庫中的代碼發 生改變時,都要執行新的構建。CI 的好處很明確:經常組裝軟件可以大大提高在 早期發現缺陷的可能性,而缺陷在早期還不復雜,容易解決。本教程是 追求代碼 質量 系列的配套文章。在本教程中,Andrew Glover 介紹持續集成的基本方面, 並講解如何用最好的開放源碼技術設置 CI 過程。
開始之前
了解本教程討論的內容以及如何從本教程獲得最大的收益。
關於本教 程
本教程討論持續集成的基本問題:什麼是持續集成,為什麼需要它,它 是如何工作的,以及 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 只需要兩個步驟:
下載最新的版本(它打包為一個 WAR 文件)。
運行 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 環境中的開發節奏應該是下面這樣:
對本地沙箱執行更新。
修改一些代碼。
編寫一些測試。
運行本地構建,從而確保您沒有破壞任何東西。
執行更新,從而確保從 SCM 獲得最新的代碼;然後再次運行構建,確保一切 正常。
簽入您的修改。
注意,如果您和團隊的其他成員同時在代碼庫中工作,那麼第 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 的插件生成各種結果的趨勢。
現在您應該有信心快速地構建高質量的軟件了!