ShippableでCI 完全マスター

## 概要
これはCIサービスである[Shippable](https://www.shippable.com/)の紹介と、[Heroku](https://www.heroku.com/)を使用したCIのための具体的な設定について説明する記事である。

### 経緯
自前でホスティングしているJenkinsのメンテとかさむAWS EC2の費用が嫌になった俺達は、クラウド型CIサービスを探す旅に出たのだった。
[Travis CI](https://travis-ci.org/)は試用してみたもののそもそもの値段で却下（でも100回分お試しは堪能しました）。[CircleCI](https://circleci.com/)は最近流行ってきていて候補の筆頭に上がったものの、以前と課金体験が変わりテスト時間短縮のために並列に走らせると値段が高くなってしまうことを知ったのでテンションが下って却下。
どうせだったら先駆者になろう（ただポッと出のサービスですぐに終了してあとで大変になるのはNG）と、[Leanstak](http://leanstack.io/)にあるサービスを漁って見つけた「安い！速い！上手い！」を兼ね備えたのがこの[Shippable](https://www.shippable.com/)である（※なお日本語の記事もぐぐればある程度見つかりました）。ちなみに昨年末[$2Mの調達](http://techcrunch.com/2013/12/10/techstars-seattle-startup-shippable-raises-2-05m-for-continuous-integration-platform-built-on-docker/)をしている。

### 注意
意欲的なサービスで抜本的に変わったりしたことがあったり、機能追加や不具合修正などが日々なされているサービスなのでその点は注意。（サポートの窓口である[Github](https://github.com/Shippable/support)の[Issues](https://github.com/Shippable/support/issues)を見るとよく分かります）

## 安い！速い！旨い！
### [安い！](https://www.shippable.com/pricing.html)
- Privateリポジトリであろうが完全無料で無制限にビルド可能
    - ただ制限として同時に走らせられるテストは1つのみ
    - しかしその制約さえ守れば、リポジトリ数に制限は無い
- 5個並列にテストを走らせられるStartupプランは $12/YEAR !
    - YEARだよYEAH！
    - でも正直いつまでこれが続くのか不安だけどね！！

### [速い！](https://www.shippable.com/features.html)
- テストに使用したコンテナはキャッシュすることが可能
    - つまりどういうことかというと、テストはDcokerで取り扱えるコンテナが作成されそこで行われるのだが、テスト終了時にコンテナごと保存し次のテスト時に使いまわすことが可能
        - GemやDBに格納されているスキーマさえまるごと保存される
        - bundle installやrake db:migrateに取られる時間が減る！
    - この機能は現V2になった時[一度無くなった](http://blog.shippable.com/v2-is-here-dedicated-hosts-custom-docker-workflows-and-a-lot-more.)のだが、数日前に[復活した](http://blog.shippable.com/faster-builds-with-cached-containers)
        - ちなみにこの機能、V1ではデフォルトで有効（オプトアウトで無効）だった
        - （ただ機能が不安定な気がするんだけど気のせいかな……）
- 安いにもある通り安価に、そして[簡単に並列テストが出来る](http://blog.shippable.com/shippable-v2-what-works-differently-from-v1)
    - （でも全てのテストが通った時のみafter_successを実行するparallelized_testフラグが効いていない気がするんだけど気のせいかな……）

### 旨い！
- もちろんHerokuへのデプロイも意識している（公式に[Heroku toolbeltを使ったデプロイサンプル](http://docs.shippable.com/en/latest/continuous_deployment.html#with-heroku-toolbelt)もあるよ！）
- 並列デプロイ時には複数のデプロイをデプロイグループとしてまとめて個別に表示
- テストやカバレッジレポートを吐き出させればそれをウェブUI上で表示させることも可能
- 一応、.travis.yml互換（Travis CIで使用しているリポジトリがそのまま動く……はず）
    - 余り入り組んだことはしていない私の過去のリポジトリはそのまま動いた

## 上記を網羅した設定例
- Ruby on Railsを想定
- PostgreSQLを想定 (Herokuなので)
    - ユーザとデータベースががまだ無い場合は作成
- キャッシュ有効
- 3並列テスト
- テストとカバレッジレポートを出力
- テスト成功後Herokuへデプロイ

```yaml:shippable.yml
language: ruby
rvm:
  - 2.1.3
cache: true
parallelized_test: true
services:
  - mongodb
script:
  - RAILS_ENV=test bundle exec rake db:migrate
  - bundle exec parallel_test spec -n $PARA_NUM_GROUPS --only-group $PARA_GROUPS --group-by filesize --type rspec
env:
  matrix:
    - "PARA_NUM_GROUPS=3 PARA_GROUPS=1"
    - "PARA_NUM_GROUPS=3 PARA_GROUPS=2"
    - "PARA_NUM_GROUPS=3 PARA_GROUPS=3"
  global:
    - COVERAGE_REPORTS=shippable/codecoverage
    - SPEC_OPTS="--format RspecJunitFormatter --out shippable/testresults/results.xml"
    - APP_NAME=heroku_application_name
    - secure: # HEROKU_API_KEY=<your key here> を ENCRYPT ENV VARIABLE 機能で暗号化してここへ
before_install:
  - which heroku || wget -qO- https://toolbelt.heroku.com/install-ubuntu.sh | sh
after_success:
  - test -f ~/.ssh/id_rsa.heroku || ssh-keygen -y -f ~/.ssh/id_rsa > ~/.ssh/id_rsa.heroku && heroku keys:add ~/.ssh/id_rsa.heroku
  - git remote -v | grep ^heroku || heroku git:remote --app $APP_NAME
  - git push heroku master
  - heroku run rake db:migrate
before_script:
  - psql -c "CREATE EXTENSION IF NOT EXISTS dblink" -U postgres
  - |
    c="DO
    \$body\$
    BEGIN
       IF NOT EXISTS (
          SELECT 1
          FROM   pg_catalog.pg_user
          WHERE  usename = 'alice') THEN
          CREATE ROLE alice LOGIN PASSWORD 'password';
       END IF;
    END
    \$body\$
    "
  - psql -c "$c" -U postgres
  - |
    c="DO
    \$body\$
    BEGIN
       IF NOT EXISTS (
          SELECT 1
          FROM   pg_database
          WHERE  datname = 'web_application_test') THEN
          PERFORM dblink_exec('dbname=' || current_database(),
                              'CREATE DATABASE web_application_test OWNER alice;');
       END IF;
    END
    \$body\$
    "
  - psql -c "$c" -U postgres

```

```rb:Gemfile
gem "parallel_tests", group: :development

group :test do
  gem 'simplecov'
  gem 'simplecov-csv'
  gem 'rspec_junit_formatter'
```

```rb:spec/spec_helper.rb
require 'simplecov'
if ENV["COVERAGE_REPORTS"]
  require 'simplecov-csv'
  SimpleCov.formatter = SimpleCov::Formatter::CSVFormatter
  SimpleCov.coverage_dir(ENV["COVERAGE_REPORTS"])
end
SimpleCov.start 'rails'
```

## おわりに
PostgreSQLでMySQLの"CREATE DATABASE IF NOT EXISTS db_name"相当のことするの面倒なんですね……（はっけん）