编者语:在这本书中,Pragmatic Project Automation, Mike Clark 为你提供了无所不有的方法来自动化你的软件项目:用Ant来一步式构建(one-step builds),用CruiseControl 按计划时间来持续构建(scheduling continuous builds),按一下按钮就可以发布软件,轻易地安装和布署应用,通过email,RSS,你的手机,甚至是熔岩灯(lava lamps)来监控构建和程序运行。方法包含示例使初学者也很容易实践,即使是熟手也有更多高级的主题能教她们一些新东西。在这篇文章里,他描绘了自动化你的项目能带来的好处的概要。
你即将要在明天早上交付一个用于关键性演示的软件版本。穿着西装的销售人员嘴里吹着泡炫耀你公司的新的辅助应用给一些十分重要的有钱人。正象你正在键盘上寻找感觉,你的老板却站到你的旁边提醒你这个演示可能会得到这个项目或者让项目完蛋。不要有任何压力!
一步构建和测试
在你为那些“必须有”的演示特征输入最后一行代码后,差不多都中午了。你最喜爱的IDE 显示你的代码编译通过并通过了单元测试。但是当他结合到系统的其余部分,你的代码是否能象预期的那样正常工作呢?为了弄清楚它,你更新了你本地的工作区,为了同步现在版本控制系统中的文件。然后你运行了项目的一步式构建过程:
$ ant
这个命令编译了所有的代码文件,运行了下面Ant构建文件中的配置的所有的单元测试。
清单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>
当你写代码时,你在你的IDE中频繁地点击方便的构建按钮来确认所有的东西都编译了。你也已经热衷于当你的JUnit测试通过后,看到一个快乐的绿色条(单元测试成功的标志),然后你使用JUnit测试运行器(JUnit test runner)整合到你的IDE中。但不是团队里的每个人都象你那样喜欢这个IDE,而且你也不想在每次有人想做一个构建时就不得不启动IDE。使用构建文件来和你的IDE分离,每个团队里的人都能一步式地持续构建和测试项目。(项目使用Maven 来创建一步式构建。)
你不要惊讶,构建成功了,你再次意识到你是世界上最伟大的程序员。 不仅是在代码里做这个构建过程带给你的信心,他也让你对项目可在你的IDE外构建有信心。
感觉非常好,你上传了已经更改的文件并避开了那些麻烦。为了准备演示,你仍旧有许多要做的,你需要更早的离开第一次去参加你儿子的tee-ball游戏比赛。时钟滴答的响着…
泡泡危机
午饭后回办公室的路上,你注意到项目的红色熔岩灯在沸腾(这种灯灯罩里有特殊液体材料)。哦!当你想离开去吃饭的时候,绿色的灯正欢快的冒着泡(说明程序一切正常)。你离开后,你项目的按计划进行的构建过程在机器上努力的尝试构建和测试现在版本控制资源(version control repository)上的代码。但发生了可怕的错误。
让你的项目连续地运行构建是很容易的,因为实际上你可以在命令行中一步就创建一个构建。这意味着你可以很容易的让一台计算机整日地为你运行构建。否则,你就不得不放一个开发人员不时地用命令行构建文件。取而代之,你用CruiseControl 设定在你的项目专用构建机器上在一定的时间间隔内自动地创建构建,如下面的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>
这个config.xml文件使CruiseControl 每5分钟被唤醒一次,检测你项目的CVS资源,看看是否需要构建。只有当你的团队中有人更改了一个已经存在的文件,或者加入了一个新文件到版本控制资源里的时候,CruiseControl 才尝试去创建一个构建。他依赖于怎样用一个Ant或者Maven构建文件为你的项目去创建一个构建。你可以用CruiseControl 设定去运行一个叫cc-build.xml 的Ant构建文件,内容如下:
清单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文件通过删除你项目在上次构建时的拷贝和从CVS资源上下载一个项目新的拷贝引导运行构建过程。然后他自动运行同样的你在命令行中编译和测试项目的bulid.xml文件。在运行build文件后,CruiseControl 发布构建结果给所有注册了的发行人。(使用Maven的项目也用了CruiseControl ,不过它被谁设定用一些你不喜欢使用的其它的版本控制系统去监视变化)
每5分钟把这些工作全都做一遍是个轻松的工作,这就是为什么你喜欢让CruiseControl 来为你做这些。当你最初设置它的时候,看起来非常麻烦,但你已经学会感激能适时回馈给你信息的价值。5分钟的计划任务不过是编译了所有的代码和运行了单元测试,作了一个快捷的健全性的检测。你也用CruiseControl 设置去运行一整套系统在不那么频繁的时间间隔里执行测试。如果5分钟构建失败了,那问题不会存在超过了5分钟。这就让你比较容易的查找和修复问题,从而节约了你宝贵的时间。如果在最近的5分钟里,没有变化被提交,那么CruiseControl 保持休眠。
看,构建失败了!CruiseControl 点燃了红色熔岩灯是件好事,因为你可能忽略了你装满了邮件的收件箱里的构建失败的email。着急找到问题的根源,你打开了构建状态web页发现匆忙间你忘了上传一个新文件。这很难为情,但至少你现在能更早的修复构建在演示前在问题复杂起来导致一个噩梦般的调试会议之前。
快速发布
不久以后,团队里的每个人都上传了他们的代码。现在你准备创建一个分发文件部署它到演示的服务器上。但在出发之前你只剩了不多的时间,发布过程包括了以下单调乏味的步骤:
1. 测试在主干路径中的代码
2. 在版本控制上建一个版本分支
3. 校对版本分支的内容
4. 构建和测试版本分支中的代码
(修复所有的问题)
5. 打包这个版本的所有的文件到一个分发文件里
6. 测试分发文件中的内容
7. 把版本控制中的版本分支作上标签
8. 把分发文件发给QA
这只是你能想起来的步骤!实际上,发布你的软件总是一个耗时的易发生错误的过程。因此,你不能经常的发布你软件的新版本。你对不得不回忆发布过程中的所有步骤和不得不正确的输入所有需要的命令的压力感到疲惫。现在你的项目一步步的发布过程都是自动的(甚至备有文档)凭借着一些按钮操作般的发布脚本。
演示将要花费一点准备,你想要一个和程序主干上活跃部分隔离的稳定的工作区。但你又不想在下一个版本发布前冻结主干阻塞每个人的开发。解决方案就是在你的版本控制资源里创建一个版本分支。第一个脚本控制着有运行版本号的发布过程的1到4的步骤:
$ release_branch 2_7_1
脚本成功地运行了,告诉你版本分支创建了所有的代码编译并通过了测试。如果有问题,你要在这个版本分支路径中进行修改,测试这些改变,提交改变给版本分支。你也可以运行其它的脚本合并这些改变到主干。
一旦你有了一个版本分支,你修复了所有的问题,你准备实际上生成一个发布版本。为了这些,你运行了另一个脚本去控制4到8的步骤,给了一个版本号。
$ release_generate 2_7_1
运行这个脚本的结果是一个独立的分发文件―客户可以安装和发布的相同的文件。你已经快完事了;在你离开之前只剩一步了。
脏部署细节
部署应用到演示服务器是另一个多步骤地手动过程,即使你慢慢做,也基本上会出错。但是因为你的团队需要频繁地部署软件―那正确可靠地部署在任何时候都很重要―你已经自动化了部署步骤。当你运行部署脚本的时候所有的脏部署细节为你执行。
$ deploy
在这种情况下,脚本传输分发文件到演示服务器解包所有的部署模块到他们各自的路径。但在脚本实际在应用服务器执行前,还有一步需要完善。
你不想自动地部署应用,只是因为一个愚蠢的配置问题而不能开始。在应用干净地运行之前,你的应用有一个配置值的数字需要适当地设置。于是在启动应用服务器之前,脚本运行了一套诊断测试来快速查明部署中任何潜在的问题。
特别的,你已经注意到搞坏了数据库配置的是一个普通的部署错误。调试这个问题让你掉了许多头发,所以上个星期你用JUnit写了下面的诊断测试:
清单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 方法尝试使用一个项目的数据库类实例去连接数据库。那个类从配置文件里读入了配置的值,就像database.url 这样的属性。如果诊断测试不能连接到数据库,那看起来在运行的时候你得应用将要遭受同样的命运了。因此,如果connect 方法在诊断测试调用时抛出一个异常,fail() 方法被调用来打印一个有用的信息来帮助你修复问题。
万分感谢,部署脚本报告没有错误。这告诉你应用已被部署了,所有的诊断测试通过了,应用服务器也被启动了。你得演示在运转了!
泄密监视器
你点击了web应用的几个页面做一个快捷健全的检测。这看起来很棒,但你想今晚知道对于驱动明天的演示来说它仍旧会很棒,这样你就可以睡好了。项目是在线的,这样你整个晚上都可以知道有什么不好的事情发生在演示上。
不要担心。你从你的自动化工具箱找个程序可以每隔几分钟搜索一个web网站中的像"Error" 或者 "Exception." 这样的有关错误的词。如果有这种词出现,或者web网站变得不可用了,监视器将发一个SMS消息到你得手机上。这样的话,如果应用当掉了,你会有比较多的时间在演示前修复它。在你跑出办公室门之前,你把监视器挂到演示站点上:
$ monitor http://demoserver:8080/whizbang
在tee-ball 的场地,你看到你得儿子走上了本垒板。其间,在你得办公室,你可靠的监视程序独自运行着。你的手机就在旁边,但他始终没响,你像个孩子似的睡了因为演示将没有故障的结束。
向前和向上
演示是如此成功以至于客户为了得到应用的拷贝一直排到了门口。提交分发文件到你公司的网站或者烧若干的光盘将拖慢你得团队,如果没有自动化脚本来控制那些任务的话,也会是同样的。如果有人恰巧报告了一个错误,你可以轻松的从版本控制中重新生成演示。当错误被修正,你可以按一下按钮生成一个新的发布。
无论你在哪儿,自动化都可以依靠问题发生时更早地通知你来帮助减少演示失败的风险。自动化也节省了你的时间,保证结果一致,通过给你提供可重复的方法来构建和部署你的软件。当你继续准备演示和发布新软件时,自动化可以多次地发挥作用。
实际的项目自动化
不幸的,这个故事对许多项目来说并不真实,也许也包括你的项目。许多团队努力用手工去做这些项目的杂事,但人们做这种重复性的工作并不如计算机做的那么好。这些团队冒险使用不同的方法运行一个过程,一次只关注一点,只在一台机器上而不在其他机器上,或者做着错误的事情。坦白的说,你知道你有比持续构建更好的事情去做,按着多步清单,拷贝文件到服务器,监视运行的程序。但你怎样才能迅速有效地把这一块块的工作衔接到一起呢?
从这篇文章看起来,自动化你现在或者下一个项目包括了大量工作。谢天谢地,你不必今天就自动化你所有的项目过程来开始明确自动化的好处。你自动化的每个项目的杂事都是一笔投资,能立即回报和随时间增加价值。你可以借助免费的可用工具如Ant,Maven,CruiseControl, JUnit, 和简单的脚本一步步地迅速地开始。Pragmatic Project Automation 这本书告诉你怎样用你的计算机一次次的用同样的方式来做你项目的重复性的任务,不再烦扰你。这意味着你将拥有更多的时间和精力去做真正令人激动―有挑战性--的事情就像写高质量的代码。