本日も乙

ただの自己満足な備忘録。

GitHub + Travis CI + CoverallsでHerokuにデプロイする

今回もしつこくHeroku+Symfony2ネタです。
前回の記事で、HerokuでSymfony2アプリケーションをデプロイできるようになりましたが、そういえばテストを通していないなと思いました。
ちゃんとしたプロダクトを作るならテストは必須!ということで、オンラインCIサービスのTravis CIと連携してテストを通した後にHerokuに自動的にデプロイできるようにしてみます。
また、テストのカバレッジレポートを画面で確認できるように、Coverallsとも連携するようにしました。

今回の目標

  • GitHubにあるSymfony2アプリケーションをTravis CI経由でHerokuにデプロイできるようにする
  • Travis CIとCoverallsと連携してテストのカバレッジを確認できるようにする

環境

  • Mac OS X 10.9.2
  • Git 1.8.5.2
  • Symfony2 (2.3.16)
  • PHP 5.5.14
  • heroku-toolbelt 3.9.6
  • travis(コマンド) 1.6.16

前提条件

Travis CIとは

簡単にいえば、オンラインでCI(継続的インテグレーション)してくれるサービスです。最近はTravis CIの他、CircleCIwerckerdrone.ioなどが多くリリースされています。

CIツールといえば、Jenkinsを思い浮かべる人も多く、 僕自身も仕事でJenkins先生には日頃からお世話になっている定番ツールですが、自前でJenkinsをインストールしたりプラグイン入れたり細かい設定をするのが多く、個人開発用に使うのは敷居が高いなと思います。

その点、オンラインCIサービスだとデプロイするのに必要なツールなどが用意されており、設定ファイルをリポジトリに置くだけで実行することができてしまいます。また、各種ツールをアップグレードしたりなどメンテナンスする必要がないのも大きなメリットです。

多くのオンラインCIサービスがありますが、今回は次の理由でTravis CIを使ってみました。

では、実際に設定していきます。

ローカル環境でテストが通るようにする

何はともあれ、テストが通らないとデプロイも通らないので、まずテストを通すようにします。
テストコードが無い人はテストコードを書いてください。

$ cd ./symfony2
$ phpunit -c app

テストが通ったら一旦、GitHubにPushしておきます。

$ git push origin master

Travis CIとGitHubを連携する

ブラウザ上で操作します。

  1. Travis CIにGitHubアカウントでログインする
  2. 右上のアカウント名 →「Accounts」をクリックする
  3. Travis CIと連携したいリポジトリの右側のスイッチを「ON」にする
    もし、表示されていない場合は、「Sync now」ボタンをクリックする

HerokuのAPIキーを暗号化する

Travis CIでHerokuへデプロイする際にAPIキーが必要ですが、後述する設定ファイル(.travis.yml)にAPIキーをそのまま記述するのはセキュリティ的に危険なので暗号化します。

travisコマンドが必要なのでインストールします。

$ gem install travis
$ travis --version
1.6.16

HerokuのAPIキーを取得します。

$ heroku auth:token
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"が生のAPIキーです。

HerokuのAPIキーを暗号化します。

$ travis encrypt -r [リポジトリ名] HEROKU_API_KEY=[HerokuのAPIキー]

# e.g.
$ travis encrypt -r ohsawa0515/heroku-symfony2 HEROKU_API_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
secure: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"

"XXX..."が暗号化されたHerokuのAPIキーとなります。

.travis.yml を編集してgit pushする

Travis CIの設定を設定ファイル(.travis.yml)で行います。
ComposerでSymfony2をインストールするとすでに.travis.yml が入っているため、それを使います。

# 言語をPHPに指定します
language: php

# PHPのバージョンを指定します
# 複数バージョンを指定すると、それぞれのバージョンに対してTravis CIのビルドが実行されます
php:
  - 5.5

# CIする対象のブランチを指定します
# PullRequestされたブランチは例外で、どんなブランチでもビルドが実行されます
branches:
  only:
    - master
    - develop

# CIする前に実行したいコマンドなどを指定します
before_script:
  # セッション管理にMemcachedモジュールを使っているため、モジュールをインストールしています
  - printf "\n" | pecl install memcached
  - echo "extension=memcached.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
  # composer.jsonに記載されているモジュールなどをインストールします
  - composer install -n --dev

# CIする際に実行したいコマンドを指定します
# ここでPHPUnitを実行しています
script:
  - mkdir -p build/logs
  - phpunit -c app

# テスト実行後にデプロイ先を設定します
deploy:
  # デプロイ先をHerokuに指定します
  provider: heroku
  # 先ほど暗号化したHerokuのAPIキーを指定します
  api_key:
    secure: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
  # デプロイしたいブランチとHerokuのアプリケーションを指定します
  # 指定した以外のブランチではHerokuにデプロイされません
  app:
    # developブランチでheroku-symfony2-0515というHerokuアプリケーションにデプロイする
    develop: heroku-symfony2-0515

これで実際に動くか確認してみます。

$ git add -A && git commit -m "add heroku deploy"
$ git push origin master

GitHub$ git pushすると自動的にTravis CIでビルドが実行され、テストが正常に通るとHerokuにデプロイされるようになります。
正常に終了すると、以下の図のように"passed"になります。

Deploy_success

Coverallsと連携する

次にテストのカバレッジをレポートしてくれるCoverallsと連携してみます。

連携するためには、Coveralls にアクセスし、GitHubアカウントでサインインします。
リポジトリ一覧が表示されるので、コードカバレッジを表示させたいリポジトリをONにすればアカウント上の連携は完了です。

composerでphp-coverallsをインストール

Coverallsを使うにはAPIを使う必要があるのですが、PHPでCoverallsを実行してくれるphp-coverallsを使います。

# composer.json
{
    "require-dev": {
        # 以下追記
        "satooshi/php-coveralls": "dev-master"
    }
}

composer installcomposer updateすればインストールは完了です。

app/phpunit.xml.dist を編集する

カバレッジを参照できるようにapp/phpunit.xml.distを編集します。

<?xml version="1.0" encoding="UTF-8"?>

<!-- http://phpunit.de/manual/current/en/appendixes.configuration.html -->
<phpunit
    backupGlobals               = "false"
    backupStaticAttributes      = "false"
    colors                      = "true"
    convertErrorsToExceptions   = "true"
    convertNoticesToExceptions  = "true"
    convertWarningsToExceptions = "true"
    processIsolation            = "false"
    stopOnFailure               = "false"
    syntaxCheck                 = "false"
    bootstrap                   = "bootstrap.php.cache" >

    <testsuites>
        <testsuite name="Project Test Suite">
            <directory>../src/*/*Bundle/Tests</directory>
            <directory>../src/*/Bundle/*Bundle/Tests</directory>
        </testsuite>
    </testsuites>
    <!-- 追記 -->
    <logging>
        <log type="coverage-clover" target="../build/logs/clover.xml"/>
    </logging>
    <!-- 追記終わり -->

    <!-- ...以下省略 -->
</phpunit>

Symfony2の場合、phpunit.xml.distapp/以下にあるため、targetのパス(../build/logs/clover.xml)に注意してください。

.travis.yml を編集する

先ほどの.travis.ymlを編集します。

language: php

php:
  - 5.5

branches:
  only:
    - master
    - develop

before_script:
  - printf "\n" | pecl install memcached
  - echo "extension=memcached.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
  - composer install -n --dev

script:
  - mkdir -p build/logs
  - phpunit -c app
  # 以下を追記
  - php vendor/bin/coveralls -v

deploy:
  provider: heroku
  api_key:
    secure: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
  app:
    develop: heroku-symfony2-0515

README.mdには、after_scriptに設定していますが、after_scriptだとHerokuでデプロイした後に実行されてしまい、何故か動かなかったので、scriptに書きました。

.coveralls.yml を作成する

Coverallsの設定を.coveralls.ymlに記述して、Symfony2ディレクトリ直下(.travis.ymlと同じ階層)に置きます。

service_name: travis
src_dir: src
coverage_clover: build/logs/clover.xml
json_path: build/logs/coveralls-upload.json
exclude_no_stmt: true

ローカルでCoverallsを実行($ php vendor/bin/coveralls -v)する場合、repo_tokenというパラメータにCoverallsにトークンを設定しますが、Travics CI経由で実行する場合はrepo_tokenパラメータを設定しません。

この辺りを勘違いしてrepo_tokenトークンを設定した状態でデプロイした際、Travis CIのログに「Requirements are not satisfied.」と表示され、Coverallsの方もカバレッジが表示されませんでした。結局、php-coverallsのソースを見てやっと原因が突き止めました。

これでリポジトリにCommitしてGitHubにPushすれば、Travis CIのビルド実行後にCoverallsでテストカバレッジが表示されるようになるはずです。

converalls_lsit

こんな風にカバレッジが表示されるようになります。カバレッジが16%と低いのはデモ用に作ったコードだからです(と言い訳してみる)。

"BUILD"列か"COMMIT"列のリンク先をクリックすると、カバレッジの詳細が表示されます。ファイル一覧も表示できてテストがどのぐらいカバーできているかも分かるようになっています。

coveralls_files_lsit

ファイルのリンク先をクリックすると、そのファイル内の詳細も見れるのですが・・・

coveralls_file_detail_1

パスが間違っているのか表示されません。
そこで、「Git repo root directory」を空にして、「Git repo sub directory」に「src」と入力してUPDATEします。

coverall_file_detail_2

パスが合えば、ソースコードが表示され、行単位でテストがカバーした範囲が分かるようになります。
赤く覆われている行はテストがカバーしていない行です。

coverall_file_detail_3

最後に

GitHubTravis CI、Coverallsと連携してSymfony2アプリケーションをHerokuにデプロイするように設定できました。
他にもライブラリのバージョンアップを通知してくれるversioneyeやアプリケーションのソースコードをスキャンして脆弱性を診断してくれるSensioLabsInsightなどもあり、自分で一から環境を構築しなくてもすぐに開発できてしまう環境ができてとても便利ですね。

多くのサービスがリリースされ、開発効率が向上できる機会が増えているので、日頃から情報収集して乗り遅れないようにしたいものです。