pockestrap

Programmer's memo

Rails 4, 5 両方対応のGemをTravisでテストした話

先日、Railsマイグレーションファイルをもっと便利に生成するGem、miのバージョン1.0.0をリリースしました。

github.com

pocke.hatenablog.com

このGemではRailsの4と5両方に対応しており、また4と5では挙動が異なります。
このため4と5両方のバージョンでテストを行う必要がありました。
しかし、通常の方法だと4あるいは5のどちらか一方でしかテストを行うことが出来ません。

Appraisal

そこで Appraisal Gem の出番です。

Appraisal を使うと複数バージョンのGemを使用したテストを簡単に実行することが出来ます。

手順

まず、development dependency に Appraisal を追加しましょう。 gemspecに以下を追記します。

Gem::Specification.new do |spec|
  # ...
  spec.add_development_dependency 'appraisal'
  # ...
end

そして、bundle installします。

次に、トップのディレクトリにAppraisalsというファイルを作成します。
このファイルの中で使用するGemのバージョンを指定します。

appraise 'rails40' do
  gem 'rails', '~> 4.0.0'
end

appraise 'rails41' do
  gem 'rails', '~> 4.1.0'
end

appraise 'rails42' do
  gem 'rails', '~> 4.2.0'
end

appraise 'rails50' do
  gem 'rails', '~> 5.0.0'
end

今回は Rails 4.0から5.0までの4つのバージョンでのテストを行うように設定しました。

ここまで終えたらappraisal installを実行します。 するとgemfiles/ディレクトリの下に各バージョンに対応するGemfileが生成され、各バージョンのGemがインストールされます。

また、.travis.ymlに以下の2点を追記します。

before_script:
  - bundle exec appraisal install


script:
  - 'bundle exec appraisal rspec'

最後に、.gitignoreに以下を追記しましょう。

gemfiles/*.gemfile.lock

公式でもlockファイルをバージョン管理から外すように書かれています。

When using Appraisal, we recommend you check in the Gemfiles that Appraisal generates within the gemfiles directory, but exclude the lockfiles there (*.gemfile.lock.) The Gemfiles are useful when running your tests against a continuous integration server such as Travis CI.

https://github.com/thoughtbot/appraisal#version-control

そしてコミットしてGitHubにpushすることで、指定した全てのRailsのバージョンでTravis上でテストが実行されます。

他の例との違い

公式のREADMEや他のブログ記事などには、Travisとの連携のときに違う方法を使うように書かれています。

# In .travis.yml
gemfile:
  - gemfiles/rails40.gemfile
  - gemfiles/rails41.gemfile
  - gemfiles/rails42.gemfile
  - gemfiles/rails50.gemfile

私も最初この方法で実行していたのですが、一つ問題が発生しました。 以下のように大量のビルドがTravisで発生してしまい、キューが詰まってしまうのです。

f:id:Pocke:20160710163058p:plain

https://travis-ci.org/pocke/mi/builds/143520513

これは、Rubyのバージョン数 * Gemのバージョン数分のビルドが走ってしまうためです。
Travisはビルド数が多くなるとキューが詰まるようで、全てビルドし終えるまでにかなりの時間がかかってしまいました。

そのため、この記事のtravis.ymlの例では、一つのタスクで全てのバージョンのテストを実行するようになっています。

f:id:Pocke:20160710163119p:plain

https://travis-ci.org/pocke/mi/builds/143533584

これによりビルド数はRubyのバージョン数のみで抑えられ、高速にビルドを終えることが可能になりました。
同じくCIサービスを作っている身としては、Travis側の負荷も抑えられてるのかなーとか思っています。

参考リンク