持續集成作為一種軟件開發中的最佳實踐被越來越多的項目組采用。因為項目在被持續的構建,團隊成員可以更早地發現代碼中被引入的錯誤,也更為方便的定位到錯誤是由誰在什麼時候提交的哪一部分代碼中引入的。由於持續地產出可部署的部件,雖然這些部件沒有實現所有的功能,但卻是可部署的,這讓項目開發的整個過程變得更為可控。同時持續集成與測試驅動開發等其他軟件開發最佳實踐的結合更能代碼健壯性和可靠性。下面我們將開始介紹如何搭建這樣一個包含對項目進行持續構建與部署的持續集成環境。
目前,在 Java 軟件構建中 Maven 已經成為了事實標准。Maven 提供了預定義流程、組建管理與大量的插件,能方便地對項目進行構建並且提供豐富的功能。同時其 XML 格式的配置文件也比較容易理解,使新手能更快的掌握 Maven 的使用。但是 Maven 也存在著幾方面的弊端。首先,XML 的配置文件雖然易懂,但當項目構建過程變的復雜的時候,配置文件會變得非常冗余和難以維護。第二,Maven 是基於插件的,構建中用到的功能全部來自於插件。當插件不提供您需要的功能時,在配置文件中您將無能為力,也就是說 Maven 的配置文件不夠靈活。第三,當沒有插件滿足您的功能需求時,您需要自己開發一個插件,而這個學習曲線是陡增的。
作為下一代項目構建工具 Gradle 使對項目的構建變得更為容易,並且提供更為強大的功能與更好的擴展性能。Gradle 提供對項目進行編譯、構建、測試、打包以及部署等功能。Gradle 具有以下特點:
基於 Groovy 之上的 DSL 的構建腳本具有非常好的擴展性並且更容易被組織
提供預定義的構建流程並且可以被容易地定制
真正支持多項目構建
提供多種方式來管理依賴並且兼容 Maven 和 Ivy 的組件庫
可以集成並支持 Ant 任務
Gradle wrapper 可以在沒有 Gradle 的環境中執行構建
Rational Team Concert(RTC)是基於 Jazz 平台的軟件家族的第一個成員。Jazz 是一個用於管理軟件開發整個生命周期的可擴展的協作平台。RTC 的架構是基於客戶端/服務端模式的,並且是跨平台的。在功能上,RTC 提供包括工作項集成、源代碼控制、報表和對構建管理的支持。RTC 為敏捷團隊帶來高度協作化的開發環境,並且針對中小規模團隊進行了優化。RTC 同時集成了與 Ration ClearCase、Ration ClearQuest 以及 SVN 等工具的互操作功能。這些功能將為團隊提供極大的可擴展性,幫助企業組建更為出色的開發團隊。
現在我們開始來搭建一個基於 Gradle 和 RTC 的持續構建與部署系統。首先對整體架構進行介紹,在後面部分再針對各個重點部分進行詳細的說明。
圖 1. 架構圖
從圖中我們可以看到,除了前文提到的 Gradle 與 RTC,還用到了其他一些工具,下面針對架構圖的分析中會說明這些工具的作用。整個構建過程由 RTC 的用戶發起,用戶向 RTC 發起針對某個構建定義的構建請求。同時構建請求也可以由定義在構建定義中的 Schedule 發起。RTC 服務器接受到構建請求後,會根據指定的構建定義與該構建定義所關聯的構建引擎,去把一個構建任務發送給構建服務器中的 Jazz Build Engine(JBE)。JBE 是一個運行在構建服務器中的服務,它會一直等待構建任務的到來,一旦收到構建任務,便會根據構建定義中的內容執行相應的構建任務。這裡,JBE 會執行定義在構建定義中的 Gradle 命令來啟動構建過程。Gradle 會完成項目的編譯、測試、歸檔等項目構建過程,並把構建產物部署到應用服務器 WebSphere Application Server(WAS)和 Nexus 上。Nexus 是一個組件管理倉庫,可以用作 Maven 中央倉庫的本地鏡像,也可以用來管理自己的組件。Gradle 對 Maven 的組件倉庫是完全兼容的。在 Gradle 執行完構建過程或則構建失敗後,JBE 會收集產生的日志並傳回給 RTC 服務器。
根據架構圖,需要安裝 RTC Server、JBE、Gradle、WAS 和 Nexus。關於各個工具的安裝方法,請參考相應的文檔。案例的構建環境有三台服務器,三台服務器安裝的操作系統是 CentOS 6.4,選用的 RTC Server 版本是 4.0.3M4 。
在安裝完 RTC Server 並配置好 RTC Client 後,第一步是配置構建定義。
在新建向導中,需要指定一個引擎 ID,這個 ID 在啟動 JBE 的時候會用到,其他參數使用默認。新建的構建引擎會出現在列表中,不過此時的狀態會提示 The build engine process may not be running, 這是因為 JBE 還沒有啟動。
下面在構建服務器中開啟 JBE。登陸構建服務器後,執行下面命令。
/opt/IBM/TeamConcertBuild/buildsystem/buildengine/eclipse/jbe -repository https://Server-1:9443/ccm/ -userId user1 -pass passw1rd -engineId FightingChickenEngine
命令中的 FightingChickenEngine 是在 RTC Client 中新建構建引擎中指定的引擎 ID,必須匹配上,否則就算啟動了 JBE,RTC 中的構建引擎也無法識別。成功啟動 JBE,再過幾分鐘時間,一般在 10 分鐘之內,RTC 會識別到 JBE,此時 RTC 中構建引擎的警告提示會消失。
下面來配置構建定義。類似與新建構建引擎,在 RTC Client 中新建構建定義。在新建向導中,選擇 Command Line – Jazz Build Engine 作為模版,並勾選 Jazz Source Control ,其他選項保持默認即可,結束新建向導後會出現構建定義的詳細配置窗口。在 Supporting Build Engines 之加入之前新建的構建引擎。
配置中的 Command 是需要 JBE 啟動的構建命令,這裡用了完整路徑,執行 build 的任務。 Working Directory 是命令執行的工作目錄,一般選擇項目的根目錄,其中 zoo 是項目名。 buildRootPath 是自定義的屬性,可以在 Properties 頁中配置。
現在用一個示例程序來介紹如何編寫 Gradle 的構建腳本。示例程序 Zoo 是一個簡單的 Java Web 應用,HTML 頁面通過 AJAX 訪問 Servlet 提供的接口,獲得隨機產生的動物和其大小,把這些動物呈現在游覽器中。
查看本欄目
為了讓您看到 Gradle 是靈活和高度可定制化的構建工具,構建腳本除了需要完成代碼編譯、資源整理、執行單元測試、打包等工作外,還有下面三個需要完成的構建過程:
Release 和 Snapshot 版本分別上傳到 Nexus 對應的倉庫
把 index.html 和 index.js 兩個文件進行合並,用戶游覽器加載完頁面後就不用再去加載 JS 文件了
Snapshot 任務構建完成後需要把應用安裝到 WebSphere Application Server 服務器
apply plugin: 'war' apply plugin: 'eclipse' sourceCompatibility = 1.6 group = 'com.fightingchicken' version = '0.1' webAppDirName = 'build/webapp-tmp' ext { textCode = 'UTF-8' uploadRepoUrl = null } dependencies { compile 'com.google.code.gson:gson:2.2.2' testCompile 'junit:junit:4.11' providedCompile 'Javax.servlet:Javax.servlet-api:3.0.1' } repositories { maven { url 'http://Server-3:8081/nexus/content/repositories/Repo/' } }
用到了 war 和 eclipse 兩個插件,指定了一些基本的參數和依賴的庫文件,使用了本地的 Maven 組件倉庫。
task release(dependsOn: [build, uploadArchives]) << { } task snapshot(dependsOn: [build, uploadArchives]) << { String target = 'root@Server-3' String targetDir = target + ':/root/zoo/' exec { commandLine 'scp','./build/libs/zoo-' + version + '.war',targetDir + 'zoo.war' } exec { commandLine 'scp', './install.py', targetDir } exec { commandLine 'ssh', target, 'wsadmin.sh -username wasadmin -password pwd -language jython -f zoo/install.py' } } gradle.taskGraph.whenReady {taskGraph → if (taskGraph.hasTask(release)) { version += '-RELEASE' uploadRepoUrl = 'http://Server-3:8081/nexus/content/repositories/Release' } else { version += '-' + new Date().format('yyyy-MM-dd-HH-mm-ss-SSS') + '-SNAPSHOT' uploadRepoUrl = 'http://Server-3:8081/nexus/content/repositories/Snapshot/' } configEnv() }
新增了 release 和 snapshot 兩個任務,兩個任務都依賴 build 和 uploadArchives ,分別完成構建和上傳任務,是插件 war 定義好的。後面使用 taskGraph 判斷了 release 和 snapshot 的存在,分別設置不同的版本號和上傳的 Nexus 倉庫的路徑。在 snapshot 任務中,上傳了 war 包和 install.py 安裝腳本到 WAS 所在的服務器中,並執行了安裝命令。
task mergeJS(type: Copy) { from 'src/main/webapp' exclude 'src/main/webapp/WEB-INF/classes' exclude 'src/main/webapp/WEB-INF/lib' into webAppDirName doLast { String indexFileName = webAppDirName + '/index.html' String indexContent = loadFileContent(indexFileName) def mc = indexContent =~ /<script src="([^"']*)"><\/script>/ mc.each() {g → String fullJSTag = g[0] String jsFileName = g[1] if(!jsFileName.startsWith('http')) { String jsContent = loadFileContent(webAppDirName + '/' + jsFileName) jsContent = String.format('<script>%s</script>', jsContent) indexContent = indexContent.replace(fullJSTag, jsContent) } } file(indexFileName).write(indexContent, textCode) } }
從這段腳本可以看到,Gradle 是非常靈活的。使用非 XML 的腳本語言來構建項目,使用者可以非常方便的加入定制的功能,而不必去開發一個插件。
查看本欄目
隨著敏捷實踐的盛行,更多的團隊在項目開發中實施持續集成。RTC 支持多種項目構建工具,如 Ant 和 Maven。作為下一代項目構建工具 Gradle,RTC 並沒有明確的對其提供支持。本文探索采用 Gradle 和 RTC 來對項目進行持續構建與部署。並通過一個案例程序詳細介紹如何搭建這樣一個持續構建系統。