作者:Mike Clark;
boool原文地址:
http://www.onjava.com/pub/a/onjava/2004/11/10/automation.html中文地址:
http://www.matrix.org.cn/resource/article/44/44061_Project+Automation.html關(guān)鍵詞: Project Automation
編者語(yǔ):在這本書中,Pragmatic Project Automation, Mike Clark 為你提供了無所不有的方法來自動(dòng)化你的軟件項(xiàng)目:用Ant來一步式構(gòu)建(one-step builds),用CruiseControl 按計(jì)劃時(shí)間來持續(xù)構(gòu)建(scheduling continuous builds),按一下按鈕就可以發(fā)布軟件,輕易地安裝和布署應(yīng)用,通過email,RSS,你的手機(jī),甚至是熔巖燈(lava lamps)來監(jiān)控構(gòu)建和程序運(yùn)行。方法包含示例使初學(xué)者也很容易實(shí)踐,即使是熟手也有更多高級(jí)的主題能教她們一些新東西。在這篇文章里,他描繪了自動(dòng)化你的項(xiàng)目能帶來的好處的概要。
你即將要在明天早上交付一個(gè)用于關(guān)鍵性演示的軟件版本。穿著西裝的銷售人員嘴里吹著泡炫耀你公司的新的輔助應(yīng)用給一些十分重要的有錢人。正象你正在鍵盤上尋找感覺,你的老板卻站到你的旁邊提醒你這個(gè)演示可能會(huì)得到這個(gè)項(xiàng)目或者讓項(xiàng)目完蛋。不要有任何壓力!
一步構(gòu)建和測(cè)試在你為那些“必須有”的演示特征輸入最后一行代碼后,差不多都中午了。你最喜愛的IDE 顯示你的代碼編譯通過并通過了單元測(cè)試。但是當(dāng)他結(jié)合到系統(tǒng)的其余部分,你的代碼是否能象預(yù)期的那樣正常工作呢?為了弄清楚它,你更新了你本地的工作區(qū),為了同步現(xiàn)在版本控制系統(tǒng)中的文件。然后你運(yùn)行了項(xiàng)目的一步式構(gòu)建過程:
$ ant這個(gè)命令編譯了所有的代碼文件,運(yùn)行了下面Ant構(gòu)建文件中的配置的所有的單元測(cè)試。
清單1:<project name="whizbang" default="test" basedir=".">
<property name="build.prod.dir" location="build/prod"/>
<property name="build.test.dir" location="build/test"/>
<property name="src.dir" location="src"/>
<property name="test.dir" location="test"/>
<property name="vendor.lib.dir" location="vendor/lib"/>
<path id="project.classpath">
<pathelement location="${build.prod.dir}" />
<pathelement location="${build.test.dir}" />
<fileset dir="${vendor.lib.dir}">
<include name="*.jar"/>
</fileset>
</path>
<target name="prepare">
<mkdir dir="${build.prod.dir}"/>
<mkdir dir="${build.test.dir}"/>
</target>
<target name="compile" depends="prepare">
<javac srcdir="${src.dir}" destdir="${build.prod.dir}">
<classpath refid="project.classpath" />
</javac>
</target>
<target name="compile-tests" depends="compile">
<javac srcdir="${test.dir}" destdir="${build.test.dir}">
<classpath refid="project.classpath" />
</javac>
</target>
<target name="test" depends="compile-tests">
<junit haltonfailure="true">
<classpath refid="project.classpath" />
<formatter type="brief" usefile="false" />
<batchtest>
<fileset dir="${build.test.dir}"
includes="**/*Test.class" />
</batchtest>
</junit>
</target>
</project>
當(dāng)你寫代碼時(shí),你在你的IDE中頻繁地點(diǎn)擊方便的構(gòu)建按鈕來確認(rèn)所有的東西都編譯了。你也已經(jīng)熱衷于當(dāng)你的JUnit測(cè)試通過后,看到一個(gè)快樂的綠色條(單元測(cè)試成功的標(biāo)志),然后你使用JUnit測(cè)試運(yùn)行器(JUnit test runner)整合到你的IDE中。但不是團(tuán)隊(duì)里的每個(gè)人都象你那樣喜歡這個(gè)IDE,而且你也不想在每次有人想做一個(gè)構(gòu)建時(shí)就不得不啟動(dòng)IDE。使用構(gòu)建文件來和你的IDE分離,每個(gè)團(tuán)隊(duì)里的人都能一步式地持續(xù)構(gòu)建和測(cè)試項(xiàng)目。(項(xiàng)目使用Maven 來創(chuàng)建一步式構(gòu)建。)
你不要驚訝,構(gòu)建成功了,你再次意識(shí)到你是世界上最偉大的程序員。 不僅是在代碼里做這個(gè)構(gòu)建過程帶給你的信心,他也讓你對(duì)項(xiàng)目可在你的IDE外構(gòu)建有信心。
感覺非常好,你上傳了已經(jīng)更改的文件并避開了那些麻煩。為了準(zhǔn)備演示,你仍舊有許多要做的,你需要更早的離開第一次去參加你兒子的tee-ball游戲比賽。時(shí)鐘滴答的響著…
泡泡危機(jī)午飯后回辦公室的路上,你注意到項(xiàng)目的紅色熔巖燈在沸騰(這種燈燈罩里有特殊液體材料)。哦!當(dāng)你想離開去吃飯的時(shí)候,綠色的燈正歡快的冒著泡(說明程序一切正常)。你離開后,你項(xiàng)目的按計(jì)劃進(jìn)行的構(gòu)建過程在機(jī)器上努力的嘗試構(gòu)建和測(cè)試現(xiàn)在版本控制資源(version control repository)上的代碼。但發(fā)生了可怕的錯(cuò)誤。
讓你的項(xiàng)目連續(xù)地運(yùn)行構(gòu)建是很容易的,因?yàn)閷?shí)際上你可以在命令行中一步就創(chuàng)建一個(gè)構(gòu)建。這意味著你可以很容易的讓一臺(tái)計(jì)算機(jī)整日地為你運(yùn)行構(gòu)建。否則,你就不得不放一個(gè)開發(fā)人員不時(shí)地用命令行構(gòu)建文件。取而代之,你用CruiseControl 設(shè)定在你的項(xiàng)目專用構(gòu)建機(jī)器上在一定的時(shí)間間隔內(nèi)自動(dòng)地創(chuàng)建構(gòu)建,如下面的config.xml文件所示:
清單2: <cruisecontrol>
<project name="whizbang" buildafterfailed="false">
<bootstrappers>
<currentbuildstatusbootstrapper
file="logs/whizbang/currentbuildstatus.txt" />
</bootstrappers>
<modificationset quietperiod="30">
<cvs localworkingcopy="checkout/whizbang" />
</modificationset>
<schedule interval="300">
<ant buildfile="cc-build.xml" />
</schedule>
<log dir="logs/whizbang">
<merge dir="checkout/whizbang/junit-results" />
</log>
<publishers>
<currentbuildstatuspublisher
file="logs/whizbang/currentbuildstatus.txt" />
<!-- email publisher -->
<!-- RSS publisher -->
<!-- lava lamp publisher -->
</publishers>
</project>
</cruisecontrol>
這個(gè)config.xml文件使CruiseControl 每5分鐘被喚醒一次,檢測(cè)你項(xiàng)目的CVS資源,看看是否需要構(gòu)建。只有當(dāng)你的團(tuán)隊(duì)中有人更改了一個(gè)已經(jīng)存在的文件,或者加入了一個(gè)新文件到版本控制資源里的時(shí)候,CruiseControl 才嘗試去創(chuàng)建一個(gè)構(gòu)建。他依賴于怎樣用一個(gè)Ant或者M(jìn)aven構(gòu)建文件為你的項(xiàng)目去創(chuàng)建一個(gè)構(gòu)建。你可以用CruiseControl 設(shè)定去運(yùn)行一個(gè)叫cc-build.xml 的Ant構(gòu)建文件,內(nèi)容如下:
清單3:<project name="cc-build" default="build" basedir="checkout">
<target name="build">
<delete dir="whizbang" />
<cvs command="co whizbang" />
<ant antfile="build.xml" dir="whizbang" />
</target>
</project>
cc-build.xml文件通過刪除你項(xiàng)目在上次構(gòu)建時(shí)的拷貝和從CVS資源上下載一個(gè)項(xiàng)目新的拷貝引導(dǎo)運(yùn)行構(gòu)建過程。然后他自動(dòng)運(yùn)行同樣的你在命令行中編譯和測(cè)試項(xiàng)目的bulid.xml文件。在運(yùn)行build文件后,CruiseControl 發(fā)布構(gòu)建結(jié)果給所有注冊(cè)了的發(fā)行人。(使用Maven的項(xiàng)目也用了CruiseControl ,不過它被誰(shuí)設(shè)定用一些你不喜歡使用的其它的版本控制系統(tǒng)去監(jiān)視變化)
每5分鐘把這些工作全都做一遍是個(gè)輕松的工作,這就是為什么你喜歡讓CruiseControl 來為你做這些。當(dāng)你最初設(shè)置它的時(shí)候,看起來非常麻煩,但你已經(jīng)學(xué)會(huì)感激能適時(shí)回饋給你信息的價(jià)值。5分鐘的計(jì)劃任務(wù)不過是編譯了所有的代碼和運(yùn)行了單元測(cè)試,作了一個(gè)快捷的健全性的檢測(cè)。你也用CruiseControl 設(shè)置去運(yùn)行一整套系統(tǒng)在不那么頻繁的時(shí)間間隔里執(zhí)行測(cè)試。如果5分鐘構(gòu)建失敗了,那問題不會(huì)存在超過了5分鐘。這就讓你比較容易的查找和修復(fù)問題,從而節(jié)約了你寶貴的時(shí)間。如果在最近的5分鐘里,沒有變化被提交,那么CruiseControl 保持休眠。
看,構(gòu)建失敗了!CruiseControl 點(diǎn)燃了紅色熔巖燈是件好事,因?yàn)槟憧赡芎雎粤四阊b滿了郵件的收件箱里的構(gòu)建失敗的email。著急找到問題的根源,你打開了構(gòu)建狀態(tài)web頁(yè)發(fā)現(xiàn)匆忙間你忘了上傳一個(gè)新文件。這很難為情,但至少你現(xiàn)在能更早的修復(fù)構(gòu)建在演示前在問題復(fù)雜起來導(dǎo)致一個(gè)噩夢(mèng)般的調(diào)試會(huì)議之前。
快速發(fā)布不久以后,團(tuán)隊(duì)里的每個(gè)人都上傳了他們的代碼?,F(xiàn)在你準(zhǔn)備創(chuàng)建一個(gè)分發(fā)文件部署它到演示的服務(wù)器上。但在出發(fā)之前你只剩了不多的時(shí)間,發(fā)布過程包括了以下單調(diào)乏味的步驟:
1. 測(cè)試在主干路徑中的代碼
2. 在版本控制上建一個(gè)版本分支
3. 校對(duì)版本分支的內(nèi)容
4. 構(gòu)建和測(cè)試版本分支中的代碼
(修復(fù)所有的問題)
5. 打包這個(gè)版本的所有的文件到一個(gè)分發(fā)文件里
6. 測(cè)試分發(fā)文件中的內(nèi)容
7. 把版本控制中的版本分支作上標(biāo)簽
8. 把分發(fā)文件發(fā)給QA
這只是你能想起來的步驟!實(shí)際上,發(fā)布你的軟件總是一個(gè)耗時(shí)的易發(fā)生錯(cuò)誤的過程。因此,你不能經(jīng)常的發(fā)布你軟件的新版本。你對(duì)不得不回憶發(fā)布過程中的所有步驟和不得不正確的輸入所有需要的命令的壓力感到疲憊。現(xiàn)在你的項(xiàng)目一步步的發(fā)布過程都是自動(dòng)的(甚至備有文檔)憑借著一些按鈕操作般的發(fā)布腳本。
演示將要花費(fèi)一點(diǎn)準(zhǔn)備,你想要一個(gè)和程序主干上活躍部分隔離的穩(wěn)定的工作區(qū)。但你又不想在下一個(gè)版本發(fā)布前凍結(jié)主干阻塞每個(gè)人的開發(fā)。解決方案就是在你的版本控制資源里創(chuàng)建一個(gè)版本分支。第一個(gè)腳本控制著有運(yùn)行版本號(hào)的發(fā)布過程的1到4的步驟:
$ release_branch 2_7_1腳本成功地運(yùn)行了,告訴你版本分支創(chuàng)建了所有的代碼編譯并通過了測(cè)試。如果有問題,你要在這個(gè)版本分支路徑中進(jìn)行修改,測(cè)試這些改變,提交改變給版本分支。你也可以運(yùn)行其它的腳本合并這些改變到主干。
一旦你有了一個(gè)版本分支,你修復(fù)了所有的問題,你準(zhǔn)備實(shí)際上生成一個(gè)發(fā)布版本。為了這些,你運(yùn)行了另一個(gè)腳本去控制4到8的步驟,給了一個(gè)版本號(hào)。
$ release_generate 2_7_1運(yùn)行這個(gè)腳本的結(jié)果是一個(gè)獨(dú)立的分發(fā)文件—客戶可以安裝和發(fā)布的相同的文件。你已經(jīng)快完事了;在你離開之前只剩一步了。
臟部署細(xì)節(jié)部署應(yīng)用到演示服務(wù)器是另一個(gè)多步驟地手動(dòng)過程,即使你慢慢做,也基本上會(huì)出錯(cuò)。但是因?yàn)槟愕膱F(tuán)隊(duì)需要頻繁地部署軟件—那正確可靠地部署在任何時(shí)候都很重要—你已經(jīng)自動(dòng)化了部署步驟。當(dāng)你運(yùn)行部署腳本的時(shí)候所有的臟部署細(xì)節(jié)為你執(zhí)行。
$ deploy在這種情況下,腳本傳輸分發(fā)文件到演示服務(wù)器解包所有的部署模塊到他們各自的路徑。但在腳本實(shí)際在應(yīng)用服務(wù)器執(zhí)行前,還有一步需要完善。
你不想自動(dòng)地部署應(yīng)用,只是因?yàn)橐粋€(gè)愚蠢的配置問題而不能開始。在應(yīng)用干凈地運(yùn)行之前,你的應(yīng)用有一個(gè)配置值的數(shù)字需要適當(dāng)?shù)卦O(shè)置。于是在啟動(dòng)應(yīng)用服務(wù)器之前,腳本運(yùn)行了一套診斷測(cè)試來快速查明部署中任何潛在的問題。
特別的,你已經(jīng)注意到搞壞了數(shù)據(jù)庫(kù)配置的是一個(gè)普通的部署錯(cuò)誤。調(diào)試這個(gè)問題讓你掉了許多頭發(fā),所以上個(gè)星期你用JUnit寫了下面的診斷測(cè)試:
清單4: public class DiagnosticTests extends junit.framework.TestCase {
public void testDatabaseConnection() {
Database database = new Database();
try {
database.connect();
} catch(RuntimeException e) {
fail("Unable to connect to the database ‘" +
database.getURL() + "‘. " +
"Please check the ‘database.url‘ property.");
}
}
}
testDatabaseConnection 方法嘗試使用一個(gè)項(xiàng)目的數(shù)據(jù)庫(kù)類實(shí)例去連接數(shù)據(jù)庫(kù)。那個(gè)類從配置文件里讀入了配置的值,就像database.url 這樣的屬性。如果診斷測(cè)試不能連接到數(shù)據(jù)庫(kù),那看起來在運(yùn)行的時(shí)候你得應(yīng)用將要遭受同樣的命運(yùn)了。因此,如果connect 方法在診斷測(cè)試調(diào)用時(shí)拋出一個(gè)異常,fail() 方法被調(diào)用來打印一個(gè)有用的信息來幫助你修復(fù)問題。
萬(wàn)分感謝,部署腳本報(bào)告沒有錯(cuò)誤。這告訴你應(yīng)用已被部署了,所有的診斷測(cè)試通過了,應(yīng)用服務(wù)器也被啟動(dòng)了。你得演示在運(yùn)轉(zhuǎn)了!
泄密監(jiān)視器你點(diǎn)擊了web應(yīng)用的幾個(gè)頁(yè)面做一個(gè)快捷健全的檢測(cè)。這看起來很棒,但你想今晚知道對(duì)于驅(qū)動(dòng)明天的演示來說它仍舊會(huì)很棒,這樣你就可以睡好了。項(xiàng)目是在線的,這樣你整個(gè)晚上都可以知道有什么不好的事情發(fā)生在演示上。
不要擔(dān)心。你從你的自動(dòng)化工具箱找個(gè)程序可以每隔幾分鐘搜索一個(gè)web網(wǎng)站中的像"Error" 或者 "Exception." 這樣的有關(guān)錯(cuò)誤的詞。如果有這種詞出現(xiàn),或者web網(wǎng)站變得不可用了,監(jiān)視器將發(fā)一個(gè)SMS消息到你得手機(jī)上。這樣的話,如果應(yīng)用當(dāng)?shù)袅耍銜?huì)有比較多的時(shí)間在演示前修復(fù)它。在你跑出辦公室門之前,你把監(jiān)視器掛到演示站點(diǎn)上:
$ monitor http://demoserver:8080/whizbang在tee-ball 的場(chǎng)地,你看到你得兒子走上了本壘板。其間,在你得辦公室,你可靠的監(jiān)視程序獨(dú)自運(yùn)行著。你的手機(jī)就在旁邊,但他始終沒響,你像個(gè)孩子似的睡了因?yàn)檠菔緦]有故障的結(jié)束。
向前和向上演示是如此成功以至于客戶為了得到應(yīng)用的拷貝一直排到了門口。提交分發(fā)文件到你公司的網(wǎng)站或者燒若干的光盤將拖慢你得團(tuán)隊(duì),如果沒有自動(dòng)化腳本來控制那些任務(wù)的話,也會(huì)是同樣的。如果有人恰巧報(bào)告了一個(gè)錯(cuò)誤,你可以輕松的從版本控制中重新生成演示。當(dāng)錯(cuò)誤被修正,你可以按一下按鈕生成一個(gè)新的發(fā)布。
無論你在哪兒,自動(dòng)化都可以依靠問題發(fā)生時(shí)更早地通知你來幫助減少演示失敗的風(fēng)險(xiǎn)。自動(dòng)化也節(jié)省了你的時(shí)間,保證結(jié)果一致,通過給你提供可重復(fù)的方法來構(gòu)建和部署你的軟件。當(dāng)你繼續(xù)準(zhǔn)備演示和發(fā)布新軟件時(shí),自動(dòng)化可以多次地發(fā)揮作用。
實(shí)際的項(xiàng)目自動(dòng)化不幸的,這個(gè)故事對(duì)許多項(xiàng)目來說并不真實(shí),也許也包括你的項(xiàng)目。許多團(tuán)隊(duì)努力用手工去做這些項(xiàng)目的雜事,但人們做這種重復(fù)性的工作并不如計(jì)算機(jī)做的那么好。這些團(tuán)隊(duì)冒險(xiǎn)使用不同的方法運(yùn)行一個(gè)過程,一次只關(guān)注一點(diǎn),只在一臺(tái)機(jī)器上而不在其他機(jī)器上,或者做著錯(cuò)誤的事情。坦白的說,你知道你有比持續(xù)構(gòu)建更好的事情去做,按著多步清單,拷貝文件到服務(wù)器,監(jiān)視運(yùn)行的程序。但你怎樣才能迅速有效地把這一塊塊的工作銜接到一起呢?
從這篇文章看起來,自動(dòng)化你現(xiàn)在或者下一個(gè)項(xiàng)目包括了大量工作。謝天謝地,你不必今天就自動(dòng)化你所有的項(xiàng)目過程來開始明確自動(dòng)化的好處。你自動(dòng)化的每個(gè)項(xiàng)目的雜事都是一筆投資,能立即回報(bào)和隨時(shí)間增加價(jià)值。你可以借助免費(fèi)的可用工具如Ant,Maven,CruiseControl, JUnit, 和簡(jiǎn)單的腳本一步步地迅速地開始。Pragmatic Project Automation 這本書告訴你怎樣用你的計(jì)算機(jī)一次次的用同樣的方式來做你項(xiàng)目的重復(fù)性的任務(wù),不再煩擾你。這意味著你將擁有更多的時(shí)間和精力去做真正令人激動(dòng)—有挑戰(zhàn)性--的事情就像寫高質(zhì)量的代碼。