PHP 持续整合简介 – 项目设定篇
2011年1月14日 Jace Ju
在前一篇安装好 CI 平台后,接着我们就要来建立一个新的 CI 项目了。
准备工作
首先在建立一个 CI 项目前,我们必须让程序代码进入版本控制系统中,我个人习惯使用 Subversion ,并在 Repository 建立 trunk 、 branches 及 tags 三个目录。
然后我会把正在最新的程序代码放在 trunk 主干下,正在开发的版本分支则放在 branches 下,最后 tags 里是放置已经稳定不再变动的版本。
以下我假设项目名称为 MyFirstProject , Subversion 的 Repository 路径就用 http://host/svn/my-first-project/trunk 做为示范。
加入建置指令文件
接着我们要在 http://host/svn/my-first-project/trunk 下加入一个 build.xml 档,到时候 Phing 会主动读取这个档案来做建置动作。 build.xml 的内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<project name="test" basedir="." default="app">
<property name="builddir" value="${ws}/build" />
<target name="prepare">
<echo msg="Prepare..." />
<mkdir dir="${builddir}" />
<mkdir dir="${builddir}/logs" />
<mkdir dir="${builddir}/logs/coverage" />
<mkdir dir="${builddir}/docs" />
<mkdir dir="${builddir}/app" />
</target>
<target name="clean">
<echo msg="Clean..." />
<delete dir="${builddir}" />
</target>
<!-- Deploy app -->
<target name="app">
<echo msg="We do nothing yet!" />
</target>
<!-- PHP API Documentation -->
<target name="phpdoc">
<echo msg="PHP Documentor..." />
<phpdoc title="API Documentation"
destdir="${builddir}/docs"
sourcecode="yes"
defaultpackagename="MHTest"
output="HTML:Smarty:PHP">
<fileset dir="${ws}/source/app">
<include name="**/*.php" />
</fileset>
</phpdoc>
</target>
<!-- PHP copy/paste analysis -->
<target name="phpcpd">
<echo msg="PHP Copy/Paste..." />
<phpcpd>
<fileset dir="${ws}/source">
<include name="**/*.php" />
</fileset>
<formatter type="pmd" outfile="${builddir}/logs/pmd.xml"/>
</phpcpd>
</target>
<!-- PHP CodeSniffer -->
<target name="phpcs">
<echo msg="PHP CodeSniffer..." />
<phpcodesniffer standard="Zend" showWarnings="false">
<fileset dir="${ws}/source">
<include name="**/*.php"/>
</fileset>
<formatter type="checkstyle" outfile="${builddir}/logs/checkstyle.xml"/>
</phpcodesniffer>
</target>
<!-- PHP dependency checker -->
<target name="pdepend">
<echo msg="PHP Depend..." />
<exec command="pdepend --jdepend-xml=${builddir}/logs/jdepend.xml ${ws}/source" escape="false" />
</target>
<!-- Unit Tests & coverage analysis -->
<target name="phpunit">
<echo msg="PHPUnit..." />
<exec command="phpunit --log-junit ${builddir}/logs/phpunit.xml --coverage-clover ${builddir}/logs/coverage/clover.xml --coverage-html ${builddir}/logs/coverage/ ${ws}/source/tests"/>
</target>
</project>
详细的 build.xml 格式介绍请参考 Phing 官方手册,这边我简单介绍一下在 build.xml 中有用到的标签。
Phing 标签简介
为 build.xml 的根元素,定义了项目的名称 (name) 、目录 (basedir) 及预设的执行动作 (default) 。
用来定义项目的属性,主要可定义属性名称 (name) 及属性值 (value) ;之后可以在整个 XML 中透过 ${property-name} 来取得该属性值。这边我定义了 builddir 属性,主要是用来放置建置后所产生的相关 log 档及文件档。
这是指要建置的目标,可以把它想成是一组命令集合。每个 target 都会有一个名称 (name) ,方便我们在执行建置动作时可以指定是否执行这个 target ;另外 target 也可以设定相依性 (depends) ,也就是在执行该 target 前,所需要先执行的 target 。
输出讯息。
删除目录或档案。
建立目录。
执行系统命令,命令要放在 command 属性里;这边是用来执行 PHP_Depend 及 PHPUnit 两个工具的命令列指令。
这是 Phing 支持 PHPDocumentor 的标签
这是 Phing 支持 PHP Copy/Paste Detector 的标签。
这是 Phing 支持 PHP_CodeSniffer 的标签。
注:其实 Phing 也有专门支持 PHP_Depend 及 PHPUnit 的标签,但我实测的结果会有问题,因此就不介绍给大家了。
另外这里有个 ${ws} 变量值得注意,它指的是 Hudson CI 项目的工作目录 (workspace) ,稍后我们会从外部把这个变量值传进来。
Phing 指令说明
以下是各个 target 的工作内容:
prepare
建立 build 数据夹以及其下的 logs 、 docs 及 app 数据夹。
clean
删除掉 build 数据夹。
app
主要的 target ,这边可以处理我们项目要布署的方式。
phpdoc
分析 source/app 数据夹下的 PHP 程序并产生 API 文件,然后将它们放在 build/docs 目录下。
phpcpd
分析整个 source 数据夹下是否有复制贴上的 PHP 程序代码,并且产生 build/logs/pmd.xml 纪录文件。
phpcs
以 Zend 型态的程序代码规范来检查整个 source 数据夹下的程序代码,并以 checkstyle 型态产生 build/logs/checkstyle.xml 纪录文件。
pdepend
呼叫 pdepend 命令来分析 source 数据夹下的类别相依性,并产生 JDepend 格式的 build/logs/jdepend.xml 纪录文件。
phpunit
呼叫 phpunit 命令来做自动化测试及分析程序覆盖率,并产生 JUnit 格式的 build/logs/phpunit.xml 自动化测试纪录文件,以及 clover 格式的 build/logs/coverage/clover.xml 覆盖率纪录文件;同时也一并产生 PHPUnit 专属的 HTML 覆盖率报表。
执行 Phing
在还没有使用 CI 之前,我们可以手动执行 Phing 来建置项目,例如:
phing prepare app phpcs phpcpd phpunit pdepend phpdoc -Dws=~/workspace
在 phing 指令后,我们可以接上任意个 target ,然后用 -Dname=value 来指定 build.xml 内会用到的 property ,例如上述的 ${ws} 。
注:别忘了把 phing 指令程序的路径加到 PATH 系统环境变量里。
简单的 Phing 就先介绍到此,准备工作也差不多了,接下来我们正式来建立 CI 项目。
建立 CI 项目
以下直接说明建立的步骤:
- 在浏览器开启 Hudson 的起始页,选择 create new jobs 以建立 CI 项目。
- 接下来在「 Job name (工作名称) 」的地方输入项目名称,例如: My First Project 。然后在下方选择「 Build a free-style software project 」后,按下「 OK 」钮。
- 跳到「 Source Code Management 」区块,选择「 Subversion 」,然后在「 Repository URL 」后输入我们项目的档案库网址:「 http://host/svn/my-first-project/trunk 」;接着在「 Local module directory 」后输入:「 source 」,这样 Hudson 就会在工作目录中产生一个 source 数据夹,并透过 svn 指令把我们的程序 checkout 到这个数据夹里。
- 再跳到「 Build Triggers 」区块,勾选「 Poll SCM 」,并在「 Schedule 」的后面输入:
5. 0 * * * *
30 * * * *
这样每隔三十分钟, CI 服务器就会启动这个建置工作。
- 接着跳到「 Build 」区块,这里我们要指定建置指令,也就是 Phing 要执行的动作。按下「 Add build step 」,选择「 Invoke Phing targets 」;在「 Phing Version 」 (如果有设定多个 Phing 版本时会出现) 的后面保持「 (Default) 」,并在「 Targets 」之后输入「 prepare app phpdoc phpcs phpcpd pdepend phpunit 」,这样 Phing 会按照顺序执行我们指定的 targets 。
接着再点选「 Advanced 」,在「 Phing Build File 」的后面留白,这样预设会读取 build.xml ;最后在「 Properties 」输入「 ws=%WORKSPACE% 」,这样就会把当前的工作目录的路径以 ws 变量带入 build.xml 。
注:在 Windows 系统下,命令列变量的格式是「 %name% 」;而在 Unin Like 系统下,而要改成「 $name 」。
如果 Phing 不是采用 PEAR 安装的话,可以在「 Phing Version 」 的地方选择手动安装的版本,例如「 Phing 2.4.4 」。不过我在 Windows 系统下测试的结果,似乎没办法正常取得 %WORKSPACE% 的值,这时就要改用另一种建构方式。
按下「 Add build step 」,选择「 Execute Windows batch command 」,然后在「 Command (命令) 」的后面输入:「 /path/to/phing.bat -f %WORKSPACE%/source/build.xml prepare app phpdoc phpcs phpcpd pdepend phpunit -Dws=%WORKSPACE% 」,这其实就是上面的动作会组成的指令。
- 最后我们要把 Phing 建置完成后的纪录文件转换成报表,这边就得靠 Hudson 的 Plugins 来帮忙。
跳到「 Post-build Actions 」区块,这边是要设定完成建置后的后续动作。
勾选「 Publish Checkstyle analysis results 」,并在「 Checkstyle results 」后输入「 build/logs/checkstyle.xml 」,这边会将 PHP_CodeSniffer 的纪录文件转换成报表。
勾选「 Publish duplicate code analysis results 」,并在「 Duplicate code results 」后输入「 build/logs/phpunit.pmd-cpd.xml 」,这边会将 PHP Copy/Paste Detector 产生的纪录文件转换成报表。
勾选「 Publish Javadoc 」,并在「 Javadoc directory 」后输入「 build/docs/ 」,这边会产生连往 API 文件档的连结。
勾选「 Publish Clover Coverage Report 」,并在「 Clover report directory 」后输入「 build/logs/coverage 」以及在「 Clover report file name 」后输入「 clover.xml 」,这样就会产生程序代码覆盖率报表。
勾选「 Publish testing tools result report 」,再点选「 Add (新增) 」后选择「 PHPUnit 」,并在「 PHPUnit Pattern 」后输入「 build/logs/phpunit.xml 」 ,这样一来就会将 PHPUnit 产生的纪录文件转换成分析报表了。
完成后就将页面拉到最下方,按下「 Save (储存) 」,这样就建立好一个 CI 项目啰。
执行项目建置
在项目首页的右方选单,我们可以点选「 Build Now (马上建构) 」来立刻执行建置工作。流程如下:
- Hudson 会更新工作区的 source ,并执行 build 动作。 build 即透过 Phing 来执行相关工具指令,并产生 log 文件或 doc 檔。
- 在 build 过程中, Hudson 会依照 Phing 执行的结果来确认 build 是否成功。
- Hudson 会在 build 执行完成之后,透过指定的 plugins 来处理这些 log 檔或 doc 档,这样我们就可以在上面看到每次建置的结果了。
结论
特续整合是确保程序质量一个很重要的环节,而透过 CI 平台的自动化建置更能减轻我们在这方面的负担;这样我们只要关心不成功的建置问题在哪里,可以更快速地找出应对的方式。
当然或许目前在 Web 开发上还不太容易看到持续整合能对我们的项目有任何立竿见影的效果,但不表示它不值得去研究。有些技术总是要踏出第一步,我们才能了解它是否真正适合我们。
继续往前进吧!