網站製作學習誌

記錄學習製作網站的一切

PHP 持續整合簡介 – 專案設定篇

前一篇安裝好 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 的內容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
<?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 標籤簡介

project

為 build.xml 的根元素,定義了專案的名稱 (name) 、目錄 (basedir) 及預設的執行動作 (default) 。

property

用來定義專案的屬性,主要可定義屬性名稱 (name) 及屬性值 (value) ;之後可以在整個 XML 中透過 ${property-name} 來取得該屬性值。這邊我定義了 builddir 屬性,主要是用來放置建置後所產生的相關 log 檔及文件檔。

target

這是指要建置的目標,可以把它想成是一組命令集合。每個 target 都會有一個名稱 (name) ,方便我們在執行建置動作時可以指定是否執行這個 target ;另外 target 也可以設定相依性 (depends) ,也就是在執行該 target 前,所需要先執行的 target 。

echo

輸出訊息。

delete

刪除目錄或檔案。

mkdir

建立目錄。

exec

執行系統命令,命令要放在 command 屬性裡;這邊是用來執行 PHP_Depend 及 PHPUnit 兩個工具的命令列指令。

phpdoc

這是 Phing 支援 PHPDocumentor 的標籤

phpcpd

這是 Phing 支援 PHP Copy/Paste Detector 的標籤。

phpcs

這是 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 來建置專案,例如:

1
phing prepare app phpcs phpcpd phpunit pdepend phpdoc -Dws=~/workspace

在 phing 指令後,我們可以接上任意個 target ,然後用 -Dname=value 來指定 build.xml 內會用到的 property ,例如上述的 ${ws} 。

註:別忘了把 phing 指令程式的路徑加到 PATH 系統環境變數裡。

簡單的 Phing 就先介紹到此,準備工作也差不多了,接下來我們正式來建立 CI 專案。

建立 CI 專案

以下直接說明建立的步驟:

  1. 在瀏覽器開啟 Hudson 的起始頁,選擇 create new jobs 以建立 CI 專案。

  2. 接下來在「 Job name (工作名稱) 」的地方輸入專案名稱,例如: My First Project 。然後在下方選擇「 Build a free-style software project 」後,按下「 OK 」鈕。

  3. 跳到「 Source Code Management 」區塊,選擇「 Subversion 」,然後在「 Repository URL 」後輸入我們專案的檔案庫網址:「 http://host/svn/my-first-project/trunk 」;接著在「 Local module directory 」後輸入:「 source 」,這樣 Hudson 就會在工作目錄中產生一個 source 資料夾,並透過 svn 指令把我們的程式 checkout 到這個資料夾裡。

  4. 再跳到「 Build Triggers 」區塊,勾選「 Poll SCM 」,並在「 Schedule 」的後面輸入:

    1
    2
    
    0 * * * *
    30 * * * *

    這樣每隔三十分鐘, CI 伺服器就會啟動這個建置工作。

  5. 接著跳到「 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% 」,這其實就是上面的動作會組成的指令。

  6. 最後我們要把 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 (馬上建構) 」來立刻執行建置工作。流程如下:

  1. Hudson 會更新工作區的 source ,並執行 build 動作。 build 即透過 Phing 來執行相關工具指令,並產生 log 檔或 doc 檔。

  2. 在 build 過程中, Hudson 會依照 Phing 執行的結果來確認 build 是否成功。

  3. Hudson 會在 build 執行完成之後,透過指定的 plugins 來處理這些 log 檔或 doc 檔,這樣我們就可以在上面看到每次建置的結果了。

結論

特續整合是確保程式品質一個很重要的環節,而透過 CI 平台的自動化建置更能減輕我們在這方面的負擔;這樣我們只要關心不成功的建置問題在哪裡,可以更快速地找出應對的方式。

當然或許目前在 Web 開發上還不太容易看到持續整合能對我們的專案有任何立竿見影的效果,但不表示它不值得去研究。有些技術總是要踏出第一步,我們才能瞭解它是否真正適合我們。

繼續往前進吧!

Comments