Technote

by sizuhiko

CakePHP2のプラグインをTravis.ciで継続的インテグレーションする

春先ぐらいからBlogの更新が滞っていましたが、その要因となったアレが大分落ち着いてきたので、たまっていたネタを順番に書き出します。

CakePHP2のプラグインを作成/公開していて、継続的インテグレーションってどうするの?と思っていました。 もちろんユニットテスト書いてあるし、素晴らしい協力者の方がPull Requestを送ってくれれば、developブランチに取り込んでテストしたりします。

Githubを徘徊していると、よく見る

これを表示したいと思ったのです。

ただCakePHP2のプラグインは当然それだけではユニットテストを実行できません。CakePHPのアプリケーションがあって、テスト用データベースとその設定があって…、ぬーん。 早い話、テスト用のアプリケーション作って、そこからテスト実行すれば良いの?ということをずっと思っていました。

CakePHPの有名プラグインはどうしているのだろう?

と思い、最初にチェックしたのはCakeDC/searchプラグイン。 大抵のアプリケーションで使う、もっともメジャーなプラグインではないかと思います。 Searchプラグインにも、あのビルド成功やダウンロード数、バージョン番号の画像が表示されています。

.travis.yml ファイルがあるので、内容を確認してみます。 すると、

before_script:
  - git clone https://github.com/burzum/travis.git --depth 1 ../travis
  - ../travis/before_script.sh
  - if [ "$PHPCS" != 1 ]; then
            echo "
                require_once APP . DS . 'vendor' . DS . 'phpunit' . DS . 'phpunit' . DS . 'PHPUnit' . DS . 'Autoload.php';
            " >> ../cakephp/app/Config/bootstrap.php;
    fi

https://github.com/burzum/travis.gitをcloneしてきて、何かやっているようです。

CakePHPプラグインを簡単にTravis.ciで継続的インテグレーションできる

早速、ページにアクセスしてみると、どうもそれはあのFriendsOfCakeからForkされたもののようです。 FriendsOfCake元リポジトリを確認します。

Easy travis setup for CakePHP plugins This repository helps easy travis integration for CakePHP plugins, primarily focused on FriendsOfCake projects, but can be used within any plugin when satisfying the requirements.

おぉ、正に私の求めていたもの。

Fabricateに導入してみた

最近作った中で、最も思い入れのあるCakePHPプラグインFabricateを継続的インテグレーションするため、上記のFriendsOfCake/travisを導入してみます。

Quick Installに書いてるとおり、以下の手順で進めます。

cd ~/develop/fabricate

git clone https://github.com/FriendsOfCake/travis.git

export PLUGIN_NAME="Fabricate"

travis/setup.sh

rm -rf travis

私がやったときはこの手順ではなかったのですが、最近変わったみたいです… まぁ細かい事は気にせずですね。

実行すると、以下のファイルが自動生成されます。

  • .travis.yml : Travis.ciで継続的インテグレーションを実行するための設定ファイル
  • Test/Case/AllFabricateTest.php : プラグインのユニットテストをすべて実行するテストスイート。すでにこの命名規則で生成されていれば作られません。

以前のバージョンでは

  • .editorconfig
  • .semver
  • .travis.yml
  • .AllPluginNameTest.php
  • composer.json
  • CONTRIBUTING.markdown
  • LICENSE.txt
  • README.markdown

が生成されていましたが、継続的インテグレーションには必要がないファイルも混在していたので、整理されたものと思います。

後はTravis.ciにサインアップして、リポジトリを追加するだけです。

実は、これが最初のビルド。PLUGIN_NAMEをexportしていなかったので、正しくテストの実行が動いていませんでした。PLUGIN_NAMEはちゃんとexportするようにした方が良いです。 .travis.ymlのPLUGIN_NAMEFabricateに設定して実行した結果が以下の画面です。

現在のtravis.gitではPHP5.4以上のようなので、問題ないのですが、当時(と言っても数ヶ月前)はPHP5.3もテスト対象になっていたので、ショートArrayシンタックスを使っている私のコードはテストが失敗していました。.travis,ymlのPHP部分を以下のように変更して

php:
  - 5.4
  - 5.5

pushすると、ビルド結果は以下のようになります。

PHPCS以外は成功しています。

PHPCSではCakePHPのコード標準がチェックされます。 CakePHPの標準コードチェックだと厳しい部分だったり、パラメータを変更してチェックさせたくないディレクトリがあったりすると思うので、PHPCS_ARGSでphpcsを実行するときのパラメータを上書きできるようになっています。

matrix:
  include:
    - php: 5.4
      env:
        - COVERALLS=1
    - php: 5.4
      env:
        - PHPCS=1
        - PHPCS_ARGS="-p -s --extensions=php --standard=ruleset.xml --ignore='*/Test/*,*/Vendor/*' ."

拡張子phpのファイルを対象とし、TestやVendorのディレクトリを対象外として実行します。 ruleset.xmlは以下のようにしました。

<ruleset name="Custom Standard">
    <rule ref="CakePHP">
        <exclude name="CakePHP.NamingConventions.ValidVariableName.PrivateNoUnderscore" />
        <exclude name="CakePHP.NamingConventions.ValidFunctionName.PrivateNoUnderscore" />
        <exclude name="CakePHP.NamingConventions.ValidVariableName.ProtectedNoUnderscore" />
        <exclude name="CakePHP.NamingConventions.ValidFunctionName.ProtectedNoUnderscore" />
        <exclude name="CakePHP.NamingConventions.ValidFunctionName.ScopeNotCamelCaps" />
        <exclude name="CakePHP.NamingConventions.ValidVariableName.MemberVarNotCamelCaps" />
        <exclude name="CakePHP.WhiteSpace.TabAndSpace" />
    </rule>
</ruleset>

CakePHPのコード標準ではprivateやprotectedの変数、関数の先頭にアンダースコアが求められるので、それを無効にしたのと、キャメルケースではない変数や関数名を使いたい箇所があったので、それを無効にしています。 最後のCakePHP.WhiteSpace.TabAndSpaceでは、以下のように=のインデント位置を合わせたコードがエラーになってしまうので、無効にしています(わかりづらいですが、=の位置が揃っています)。

public function __construct($name) {
    $this->name  = $name;
    $this->items = [];
}

どうなっているのか?

プラグインのリポジトリには.travis.ymlが追加されるぐらいです。 この設定内で、FriendsOfCakeのtravisリポジトリがcloneされ、以下に記述されている手順が実行されます。

before_script:
  - git clone -b master https://github.com/FriendsOfCake/travis.git --depth 1 ../travis
  - ../travis/before_script.sh

script:
  - ../travis/script.sh

after_success:
  - ../travis/after_success.sh

実行されるパターンは.travis.ymlに記述されているphpenv-matrixの組み合わせと、matrixのパターンになります。

  • PHP5.4, DB=mysql CAKE_VERSION=2.4
  • PHP5,4, DB=mysql CAKE_VERSION=2.5
  • PHP5.5, DB=mysql CAKE_VERSION=2.4
  • PHP5,5, DB=mysql CAKE_VERSION=2.5
  • PHP5.4, COVERALLS=1
  • PHP5.4, PHPCS=1

この6パターン毎に、before_scriptscriptafter_successが呼び出されます。

事前準備

before_script.shは実行準備のためのスクリプトで、PHPCSが1にセットされている場合は、以下のとおりpearチャンネルからCakePHPのコード標準とphpcsが依存関係としてインストールされます。

if [ "$PHPCS" = '1' ]; then
    pear channel-discover pear.cakephp.org
    pear install --alldeps cakephp/CakePHP_CodeSniffer
    phpenv rehash
    exit 0
fi

続いて、CakePHP本体が1つ親のディレクトリにインストールされます。Travis.ciではプラグインのリポジトリが最初にcloneされるのですが、結果として以下のようなディレクトリ構成となります。

  • $HOME/sizuhiko/Fabricate : プラグイン本体
  • $HOME/sizuhiko/travis : FriendsOfCakeのtravis.gitクローン先
  • $HOME/sizuhiko/cakephp : CakePHP本体
  • $HOME/sizuhiko/cakephp/app/Plugin/$PLUGIN_NAME : プラグイン本体がコピーされる先

上記ディレクトリ構成ができた後で、composer で依存関係を解決します。COVERALLS用のカバレッジレポートも生成されるようにファイルが準備されます。

実行

script.shがPHPCSが1の場合はphpcsを、それ以外の場合はユニットテストを実行します。

実行が成功したら

after_success.shがCOVERALLSのカバレッジレポートを生成します。

そして現在

最初のスクリーンショットがFabricateの現在の状態です。すべて成功しています。

最後にREADMEにバッジを表示させる方法を紹介します。

Travis.ciのバッジを表示する

Travis.ciの画面にアクセスして

build:failingのように表示されている画像をクリックします。

ブランチを選択して、表示形式からマークダウンを選択したら、表示内容をREADME.mdに貼り付けます。

カバレッジのバッジを表示する

カバレッジはCOVERALLSというサービスを使って表示します。

サイトにアクセスして、サインアップはGithubのアカウントでできるの簡単です。

サインアップ後に、上部のメニュー表示からREPOSを選択して、

ADD REPOをクリックしてリポジトリを追加します。 その後、追加したリポジトリのページを表示します。

今度は表示される画像ではなく、GET BADGE URLというボタンをクリックします。このあたりのUIは統一されると嬉しいですね…

いろいろな記述形式のコードが表示されるのでマークダウンのコードを選択してREADME.mdに貼り付けます。

ダウンロード数、バージョンを表示する

ここからはCIとは関係ないのですが、よく見るバッジであるダウンロード数とバージョン番号も表示してみたいと思います。

Badge Poserというサイトにアクセスします。

ダウンロード数やバージョン番号は、Packagistで表示されている情報を画像に変換するので、リポジトリがPackagistに登録されていることが条件となります。 Show the markdown for your Badgesにダウンロード数を表示したいPackagistのパッケージ名を入力します。

表示形式からマークダウンを選択して、コードをREADME.mdに貼り付けます。

最後に

CakePHPのプラグインを継続的インテグレーションする方法は、実はとても簡単でした。 これを機に、まだプラグインを継続的インテグレーションしていないそこのあなたも、Travis.ciとCOVERALLSを使って継続的インテグレーションをしてみましょう。