最近一段時間一直對CI工具非常關注,正好前兩天終於有點時間,把這個好好用了一下,所以寫了這篇文檔。
- 要想用得先下載,地址就不提了,佔地兒。我下載的是最新版本cruisecontrol-bin-2.3.1.zip,如果不是研究的話,建議最好下載一個bin包,這樣能省去很多麻煩。解壓後我們能看到一些目錄。
其中CruiseControl(以下簡稱CC)內建ant1.6.3;文檔在docs目錄下,這裡麵包括config.xml的相關的參數設定說明;logs下麵包括日誌資訊,可以通過在config.xml中指定日誌路徑和名稱;projects下面放的是需要進行持續整合的項目,jarkarta-commons是使用apache的math項目作例子;lib目錄中放有cruisecontrol.jar和其他運行需要的jar;webapps下是cruisecontrol build結果的網站,可以通過訪問 http://127.0.0.1:8080/cruisecontrol 來查看build的結果;在build後會產生artifacts目錄,這個目錄用來存放發布的工件。
- 使用cruisecontrol前的準備工作
- 建立CVS管理項目源碼
建議最好使用CVS管理源碼,好處在這裡就勿需多言了。具體建立的過程可以參考相關的《CVS使用手冊》,這裡需要注意的是module的設定,可以參照相應的文檔來設定。
- 設定ANT環境變數
ANT的使用也不是本篇所要闡述的,在這裡只是說一些可能碰到的情況。在按照ANT的使用指南配置好後,可以在其他環境下試一下是否好用,如果你的JDK比較新的話最好也用比較新的ant,這裡使用的是CC內建的ant,本文的環境是JDK5.0+Ant1.6.3+Eclipse3.1+Tomcat5.5,OS:WinXP SP2
- 編寫項目的build.xml
接下來用需要編寫的是ant的build.xml檔案,在本文中,我編寫了這樣的一個例子,為了便於理解,target都採用了中文,接下來我們會看到。另外,在編寫build檔案的時候,我還發現一個問題,我編寫的build檔案怎麼也不好用,只要一Run as ant,就會報異常,而在命令提示字元下運行ant則沒有這個問題,經過一段時間檢查,發現原來是Eclipse所帶的ant版本比較低,後來下載新的ant後沒有更新eclipse的ant目錄,需要進行如下的設定:
- Checkout項目到CC_HOME/projects目錄
配置好CVSROOT後,在CC_HOME/projects下執行$ cvs checkout module_name 具體的配置方法在這裡不說了。這是需要手工作的,否則CC是不會自動監測變更的。
- 修改config.xml
根據實際項目的情況修改config.xml的情況,我們下面會用一個例子來說明。
- 運行cruisecontrol
在作完上面的工作後,我們就可以啟動CC了,如果是在unix平台下需要執行
$ CC_HOME/ cruisecontrol.sh ;如果是在windows平台下執行cruisecontrol.bat,成功後會出現如下的介面:
螢幕上出現Project commons-math started和BuildQueue started提示就說明已經正確的執行了CC。
在執行後,我們可以通過IE查看build情況,介面如下:
下面顯示的是build的結果,現在的結果是BUILD FAILED,因為寫這篇文檔時網路不通,所以無法串連到apache上獲得程式進行編譯,就出現了錯誤。
我們可以看到,CC還提供了RSS訂閱的功能,藉助RSS閱讀器,我們可以方便的獲得最新的構建資訊。介面如下:
好,我們見過了CC的使用過程,下面讓我們通過一個例子來實現這個過程。
- 例子
在這個例子中,我們建立了一個項目,項目的名字叫hello,項目的結構如下:
main/src/java/com/test/qik/HelloMaven.java:樣本的程式
package com.test.qik;public class HelloMaven {public static boolean main(String[] args) {System.out.println("Hello, maven!");return true;}public void testa() throws Exception {System.out.println("test,發行就緒到生產庫的版本");}}main/test/java/com/test/qik/HelloMavenTest.java:樣本程式的單元測試用例package com.test.qik;import junit.framework.TestCase;public class HelloMavenTest extends TestCase {public static void main(String[] args) {}public HelloMavenTest(String arg0) {super(arg0);}protected void setUp() throws Exception {super.setUp();}protected void tearDown() throws Exception {super.tearDown();}/* * Test method for 'com.test.qik.HelloMaven.main(String[])' */public void testMain() {HelloMaven hm = new HelloMaven();assertEquals(hm.main(null),true);}}target/:輸出路徑build.xml:ant的構建檔案檔案內容如下:==============================================================<?xml version="1.0" encoding="UTF-8"?><project default="CI" name="hello" basedir="."><!--設定參數 --><property name="項目中文名稱" value="測試cc項目"></property><property name="project_folde"value="E:/cruisecontrol-2.3.1/projects/hello"></property><property name="來源程式目錄" value="${project_folder}/main/src"></property><property name="測試程式目錄" value="${project_folder}/main/test"></property><property name="編譯器目錄" value="${project_folder}/target/classes"></property><property name="lib目錄" value="${project_folder}/target/lib"></property><property name="單元測試報告目錄"value="E:/cruisecontrol-2.3.1/projects/hello/test-reports"></property><property name="單元測試報告檔案名稱" value="junit-noframes.html"></property><property name="CVS使用者名稱" value="administrator"></property><property name="CVS密碼" value="patterns"></property><property name="CVS地址" value="127.0.0.1"></property><property name="CVS倉庫" value="/cvsserver"></property><property name="CVS模組" value="hello"></property><property name="CVSLib模組" value="target"></property><property name="CVS串連符"value=":pserver:${CVS使用者名稱}:${CVS密碼}@${CVS地址}:2401:${CVS倉庫}"></property><property name="CVS檢出目錄" value="E:/cruisecontrol-2.3.1/projects"></property><property name="郵件伺服器地址" value="smtp.xxxxx.com"></property><property name="發送郵件使用者" value="xxx"></property><property name="發送郵件密碼" value="xxxxxx"></property><!—這裡應該輸入明文的使用者名稱和密碼,樣本中不輸入--><property name="接受報告使用者郵件清單" value="xxx@xxxx.com"></property><property name="發送使用者郵箱地址" value="xxx@xxxx.com"></property><property name="產生jar檔案"value="E:/cruisecontrol-2.3.1/projects/hello/target/hello.jar"></property><property name="產生jar檔案的基礎路徑"value="E:/cruisecontrol-2.3.1/projects/hello/target/classes"></property><!--持續整合流程 --><target name="CI" depends="初始化,擷取源碼,編譯源碼,運行測試,產生jar" /><!--1.初始化目標目錄,將目標目錄清空,建立來源程式目錄,測試程式目錄和class目錄 --><target name="初始化"><echo>正在刪除來源程式目錄...</echo><delete dir="${來源程式目錄}" /><echo>正在建立來源程式目錄...</echo><mkdir dir="${來源程式目錄}" /><echo>正在刪除測試程式目錄...</echo><delete dir="${測試程式目錄}" /><echo>正在建立測試程式目錄...</echo><mkdir dir="${測試程式目錄}" /><echo>正在刪除編譯器目錄...</echo><delete dir="${編譯器目錄}" /><echo>正在建立編譯器目錄...</echo><mkdir dir="${編譯器目錄}" /><echo>正在刪除lib目錄...</echo><delete dir="${lib目錄}" /><echo>正在建立lib目錄...</echo><mkdir dir="${lib目錄}" /><echo>正在刪除單元測試報告目錄...</echo><delete dir="${單元測試報告目錄}" /><echo>正在建立單元測試報告目錄...</echo><mkdir dir="${單元測試報告目錄}" /></target><!--2.從cvs中擷取程式源碼 --><target name="擷取源碼" depends=""><echo>從CVS中擷取源碼...</echo><cvs cvsRoot="${CVS串連符}" command="export -f -r HEAD ${CVS模組}"dest="${CVS檢出目錄}" /></target><target name="擷取lib"><echo>從CVS中擷取lib目錄...</echo><cvs cvsRoot="${CVS串連符}" command="export -f -r HEAD ${CVSLib模組}"dest="${CVS檢出目錄}" /></target><!--3.編譯器產生目標類 --><target name="編譯源碼" depends=""><echo>編譯器...</echo><javac classpathref="編譯路徑" fork="true" memorymaximumsize="128m"destdir="${編譯器目錄}" debug="true" deprecation="false"failonerror="false" verbose="false"><src path="${來源程式目錄}" /><src path="${測試程式目錄}" /><include name="**/*.java" /></javac></target><!--4.運行JUnit測試 --><target name="運行測試" depends="" description="執行單元測試"><echo>運行單元測試用例...</echo><junit><classpath refid="單元測試時路徑" /><formatter type="xml" /><batchtest todir="${單元測試報告目錄}"><fileset dir="${編譯器目錄}"><exclude name="**/TestTools.class" /><include name="**/**Test**.class" /></fileset></batchtest></junit><echo>產生單元測試報告...</echo><junitreport todir="${單元測試報告目錄}" description="產生單元測試報告"><fileset dir="${單元測試報告目錄}"><include name="TEST-*.xml" /></fileset><report format="noframes" todir="${單元測試報告目錄}" /></junitreport></target><!--5.發郵件 --><target name="發送測試報告郵件" depends=""><echo>發送測試報告郵件...</echo><mail mailhost="${郵件伺服器地址}"subject="單元測試報告 : ${項目中文名稱} - ${DSTAMP}" user="${發送郵件使用者}"password="${發送郵件密碼}" tolist="${接受報告使用者郵件清單}"messagefile="${單元測試報告目錄}/${單元測試報告檔案名稱}"><from address="${發送使用者郵箱地址}" /><fileset file="${單元測試報告目錄}/${單元測試報告檔案名稱}" /></mail></target><!--6.打包成jar檔案 --><target name="產生jar" depends=""><echo>打包成jar檔案...</echo><jar destfile="${產生jar檔案}" basedir="${產生jar檔案的基礎路徑}" /></target><!--編譯過程中用到的路徑 --><path id="編譯路徑"><pathelement path="${編譯器目錄}" /><path refid="編譯時間lib路徑" /></path><path id="編譯時間lib路徑"><fileset dir="${lib目錄}"><include name="**/*.jar" /></fileset></path><!--單元測試時用到的路徑 --><path id="單元測試時路徑"><path refid="編譯時間lib路徑" /><pathelement path="${編譯器目錄}" /></path></project>==============================================================對config.xml設定的內容<cruisecontrol> <project name="hello"> <listeners> <currentbuildstatuslistener file="logs/hello/status.txt" /> </listeners> <bootstrappers> <cvsbootstrapper localWorkingCopy="projects/hello" /> </bootstrappers> <!—監控本地工作目錄,如果在下面的目錄中發生變化則自動執行下面的build.xml,沒有變化則不再執行下面的構建工作 --> <modificationset quietperiod="30" > <!—監控前的靜默 --> <cvs localWorkingCopy="projects/hello"/> <cvs localWorkingCopy="projects/hello/main/src/java/com/test/qik"/> </modificationset> <!—計劃時間間隔,單位秒 --> <schedule interval="30" > <ant anthome="apache-ant-1.6.3" buildfile="projects/hello/build.xml" target="CI"/><!—構建時執行的build.xml --> </schedule> <log> <merge dir="projects/hello/test-reports"/> </log><!—記載日誌報告的位置 --> <publishers> <artifactspublisher dest="artifacts" file="projects/hello/target/hello.jar"/> </publishers><!—發布jar包的位置,我都是在build.xml中將jar包打好放在這個位置 --> </project></cruisecontrol>CC_HOME/docs/main/configxml.html這個檔案是config.xml的協助內容,我們可以從這裡面找到所有的配置。
- 總結
我們可以看到整個過程還是很簡單的,通過CC,我們可以部分減輕CI的工作量,但可以看到核心的構建工作還是在project的build.xml中,如果CC能提供常見的模版就更好了(呵呵,我還是想偷懶)。另外CC的監控本地工作目錄的功能有些弱智,比如我唯寫了"projects/hello",則它只認為在”project/hello”目錄下的檔案有更新才啟動build過程,如果我們更新的是下面的子目錄,則不能自動監控。
Writer by wonder
2005-12-02