Bitbucket + Werckerを使ってプライベートリポジトリで無料CI（PHPUnit, MySQL)

PHPでWebアプリを作っています．
ケチなので全部タダでやりたくてbitbucketを使っています．
今回，CIをやりたくなったのでタダでできるCIサービスを探したらWerckerが良さそうとのことだったので使ってみました．
PHP用の情報が少なくて苦労しました（最新版の公式ドキュメントの言語別ページにはそもそもphpが無かった）．

# 構成
メインのアプリはprivateで開発したいのでbitbucketのプライベートリポジトリを使いつつ，
一部のコードをライブラリ化してgithubで公開しています．
なので，

- メインのリポジトリはBitbucketのプライベートリポジトリ
- submoduleとしてgithubのパブリックリポジトリを参照している

という構成になっています．

また，PHPを使っていて，フレームワークはFuelPHP，PHPUnit用のテストコードがあります．DBはMySQLです．（この記事の内容は他のフレームワークにも応用できます）

bitbucketにpushするたびにPHPUnitを走らせるCI環境を作りたいと思います．

# Werckerの登録・設定
werckerに登録してアプリケーションの設定をしていきます．
この辺は他のページに解説があるので詳細は省略．
1.Choose a Git provider ではメインのアプリがbitbucketなのでbitbucketを選びます．
![スクリーンショット 2015-08-05 18.35.47.png](https://qiita-image-store.s3.amazonaws.com/0/65141/fc70fba2-9f73-7c61-3a94-787b95c953f0.png)

4.Configure access ではrecommendされたとおり，

```
Add the deploy key to the selected repository for me
Not recommended if you use submodules
```
を選びます．これで，WerckerがSSHキーペアを作成して，bitbucketのリポジトリのデプロイキーに勝手に登録してくれます．
![スクリーンショット 2015-08-05 18.36.38.png](https://qiita-image-store.s3.amazonaws.com/0/65141/b9777b26-ba5a-c721-6a3e-0519ba879cd3.png)


# wercker.yml作成
まず最終的なwercker.ymlを載せておきます．

```yaml
box: boedy/wercker-php
services:
  - id: mysql
    env:
      MYSQL_ROOT_PASSWORD: rootpasswd
      MYSQL_USER: testuser
      MYSQL_PASSWORD: testpasswd
      MYSQL_DATABASE: testdb

build:
  steps:
    - add-ssh-key:
        keyname: MYPACKAGE_KEY
        host: github.com
    - add-to-known_hosts:
        hostname: github.com
        fingerprint: 16:27:ac:a5:76:28:2d:36:63:1b:56:4d:eb:df:a6:48
    - script:
        name: Submodules
        code: |
          git submodule init
          git submodule update
    - script:
        name: Update apt
        code: |
          sudo apt-get update
    - script:
        name: Install phpunit
        code: |
          sudo apt-get install -y phpunit
    - script:
        name: Install dependencies
        code: |
          sudo apt-get install -y php5-mysql curl libcurl3 libcurl3-dev php5-curl
          docker-php-ext-install bcmath
          docker-php-ext-install pdo_mysql curl
          composer install
    - script:
        name: Create config directory
        code: |
          mkdir fuel/app/config/test
    - create-file:
        name: Create DB config file
        filename: fuel/app/config/test/db.php
        overwrite: true
        content: |-
          <?php
          return array(
              'default' => array(
                  'connection'  => array(
                      'dsn'        => 'mysql:host=$MYSQL_PORT_3306_TCP_ADDR;port=$MYSQL_PORT_3306_TCP_PORT;dbname=testdb',
                      'username'   => 'testuser',
                      'password'   => 'testpasswd',
                  ),
              ),
          );
    - script:
        name: echo DB configuration
        code: |
          cat fuel/app/config/test/db.php
    - script:
        name: Migration
        code: |-
          FUEL_ENV=test php oil r migrate
    - script:
        name: echo PHP information
        code: |-
          php -v
          php -i
    - script:
        name: Run phpunit
        code: |-
          php oil test
```

これを作る過程でいちいちハマりポイントがありました．
１つずつ潰していきます．

## box指定
http://old-devcenter.wercker.com/articles/languages/php.html
にPHP用の設定の仕方が書いてあるのでこの通りに進めてみた…ら，１行目

```yml
box: wercker/php
```
でいきなりエラー．
`Failed step: setup environment - GET https://registry.hub.docker.com/v1/repositories/wercker/php/images returned 404`
だそうで．

現在WerckerはDockerベースのversion2になっているらしく，`box: wercker/php`はもう古いみたい．しかし，新しいバージョンのドキュメントにはPHPの例が無い（ http://devcenter.wercker.com/docs/languages/index.html ）
どうも，Docker Hubのリポジトリを指定してやればよいみたい．探してみたらWercker用のPHPコンテナ？ https://registry.hub.docker.com/u/boedy/wercker-php/ があったので，これを指定．

```yml
box: boedy/wercker-php
```

## service指定
mysqlを使いたくて http://old-devcenter.wercker.com/articles/services/ を参考に

```yaml
services:
  - wercker/mysql
```

とかやっても，これも古いのでエラー．

```yaml
services:
  - id: mysql
    env:
      MYSQL_ROOT_PASSWORD: rootpasswd
      MYSQL_USER: testuser
      MYSQL_PASSWORD: testpasswd
      MYSQL_DATABASE: testdb
```

が正解．これで https://registry.hub.docker.com/_/mysql/ を使ってくれる． http://devcenter.wercker.com/docs/services/mariadb.html にmariadbの例が書いてあったので，docker hubの説明に加えてそれも参考にした．
ちなみに`MYSQL_ROOT_PASSWORD`以外の環境変数はオプションだが，これらを指定しておくと勝手にDBを作ってくれるので便利．

`wercker php`とか`wercker mysql`とかでググるとまだ古い情報が出てきてしまうので迷ってしまった．

## git submodule

```yaml

build:
  steps:
    - script:
        name: Submodules
        code: |
          git submodule init
          git submodule update
```
とかやると，

```
Cloning into 'fuel/packages/hoge'...
Host key verification failed.
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.
Clone of 'git@github.com:fuga/hoge.git' into submodule path 'fuel/packages/hoge' failed
```
でエラー．
SSH Keyの設定をしなければいけません．

### SSH key pairの作成
これも古い記事ですが，http://blog.wercker.com/2013/12/03/Using-private-repos.html を参考にSSH key pairを作ります．

1. Application settings -> SSH keys
![スクリーンショット 2015-08-05 19.24.27.png](https://qiita-image-store.s3.amazonaws.com/0/65141/c10a9735-0d02-25b6-9fea-af4ce4c1701b.png)

2. 公開鍵をgithubのリポジトリのデプロイキーとして登録．

3. 1.で作ったSSH key pairを，環境変数に設定
Application settings -> Environment variables
![スクリーンショット 2015-08-05 19.25.29.png](https://qiita-image-store.s3.amazonaws.com/0/65141/3d2b0ccd-42d8-c242-4f2a-8ef91ce498bd.png)
環境変数名を適当に決めて(`MYPACKAGE_KEY`とか)，SSH Key Pairを選んで1.で作ったキーペアを指定．

4. 3で設定した環境変数名でwercker.ymlのbuildセクションの先頭に追記

```yaml
build:
  steps:
    - add-ssh-key:
        keyname: MYPACKAGE_KEY
        host: github.com
    - add-to-known_hosts:
        hostname: github.com
        fingerprint: 16:27:ac:a5:76:28:2d:36:63:1b:56:4d:eb:df:a6:48
```


ここで，http://blog.wercker.com/2013/12/03/Using-private-repos.html には`add-ssh-key.keyname`しか言及がないが，http://devcenter.wercker.com/docs/git/submodules.html も参考にして`host`や`add-to-known_hosts`セクションも記述しないとうまくいかない．（Bitbucketだけ使うなら`add-ssh-key.keyname`だけでもいいのかもしれない．）
~~また，現在このページに書いてあるFinger printは間違っているので https://help.github.com/articles/what-are-github-s-ssh-key-fingerprints/ を参照して正しいフィンガープリントに書き換える．~~ 直ったみたいですね．

## PHPUnitのインストール
PHPUnitがインストールされていない（`PHP box with composer and phpunit installed`って書いてあったのに…）．まだ開発中だからですかね．
なので自前で用意します．


```yaml
    - script:
        name: Update apt
        code: |
          sudo apt-get update
    - script:
        name: Install phpunit
        code: |
          sudo apt-get install -y phpunit
```

## 依存パッケージのインストール
基本的にはcomposerでインストールします．
その他いくつかaptで入れます．

```yaml
    - script:
        name: Install dependencies
        code: |
          sudo apt-get install -y php5-mysql curl libcurl3 libcurl3-dev php5-curl
          docker-php-ext-install bcmath
          docker-php-ext-install pdo_mysql curl
          composer install
```

今回，利用しているとあるパッケージ（`"moontoast/math": "*"`）をcomposerでインストールする際にPHPエクステンションbcmathが無いと言われた

```
Installing dependencies (including require-dev)
Your requirements could not be resolved to an installable set of packages.

  Problem 1
    - moontoast/math dev-master requires ext-bcmath * -> the requested PHP extension bcmath is missing from your system.
    - moontoast/math 1.1.0 requires ext-bcmath * -> the requested PHP extension bcmath is missing from your system.
    - moontoast/math 1.0.1 requires ext-bcmath * -> the requested PHP extension bcmath is missing from your system.
    - moontoast/math 1.0.0 requires ext-bcmath * -> the requested PHP extension bcmath is missing from your system.
    - Installation request for moontoast/math * -> satisfiable by moontoast/math[1.0.0, 1.0.1, 1.1.0, dev-master].
```

ので，

```
          docker-php-ext-install bcmath
```

でインストールしています．

また，mysqlをservicesに追加したもののphpのmysqlドライバは入っていないので入れます．
これがないとDBを使ったテストで
`PDOException: could not find driver`
とか言われます．
`docker-php-ext-install pdo_mysql`だけではダメで，`sudo apt-get install -y php5-mysql`も必要なことに注意．

さらに，今回はcurlも入れています．

## 設定ファイル作成
今回使用しているフレームワーク，FuelPHPのDB接続設定ファイルを作成します．FuelPHPはこんなフォーマットですが，他のフレームワークでやるときもこんな感じでいいのではないでしょうか．
werckerにはファイルを作成するstep`create-file`が用意されている(詳細は https://github.com/wercker/step-create-file )ので，それを利用します．ディレクトリ作成のstepはないようなので直接mkdir．
DBの接続情報は上で行ったmysqlの設定の通りに指定．hostとportは`MYSQL_PORT_3306_TCP_ADDR`と`MYSQL_PORT_3306_TCP_PORT`という環境変数を参照します．

```yaml
    - script:
        name: Create config directory
        code: |
          mkdir fuel/app/config/test
    - create-file:
        name: Create config file of DB
        filename: fuel/app/config/test/db.php
        overwrite: true
        content: |-
          <?php
          return array(
              'default' => array(
                  'connection'  => array(
                      'dsn'        => 'mysql:host=$MYSQL_PORT_3306_TCP_ADDR;port=$MYSQL_PORT_3306_TCP_PORT;dbname=testdb',
                      'username'   => 'testuser',
                      'password'   => 'testpasswd',
                  ),
              ),
          );
```

## テスト実行
DBマイグレーションを実行して，PHPUnitを呼びます．
FuelPHPなのでテストコマンドが`oil`というコマンドにラップされていますが，他のフレームワークの場合は適宜`phpunit`コマンドを直接呼んだりそのフレームワークのコマンドに書き換えれば良いと思います．

```yaml
    - script:
        name: Migration
        code: |-
          FUEL_ENV=test php oil r migrate
    - script:
        name: Run phpunit
        code: |-
          php oil test
```