Rails プロジェクトの CircleCI 2.0 移行でハマった点と解決策

2018年8月31日を最後に、CircleCI 1.0 が使えなくなることがアナウンスされました。

CircleCI 1.0 End of Life on August 31, 2018

ハマったポイント

1. rspec_junit_formatter がロードできないと言われる

$ #!/bin/bash -eo pipefail
bundle exec rspec --profile 10 \
                  --format RspecJunitFormatter \
                  --format progress \
                  --out test_results/rspec.xml \
                  $(circleci tests glob "spec/**/*_spec.rb" | circleci tests split --split-by=timings)

bundler: failed to load command: rspec (/home/circleci/sbj-os/vendor/bundle/ruby/2.4.0/bin/rspec)
LoadError: cannot load such file -- rspec_junit_formatter

対応

Gemfile に sj26/rspec_junit_formatter を追加する。

公式ドキュメントを読めばちゃんと書いてあるのだけれど、CircleCI の設定を変える時に Gemfile を触るイメージがないのでハマりやすい。

If they succeed, it stores the test results using store_test_results so we can quickly see our build failures in the Test Summary section. This is why we added RspecJunitFormatter to our Gemfile.

2. RSpec を並列実行時に異常終了する

$ #!/bin/bash -eo pipefail
mkdir /tmp/test-results
TEST_FILES="$(circleci tests glob "spec/**/*_spec.rb" | circleci tests split --split-by=timings)"
bundle exec rspec --format progress \
                  --format RspecJunitFormatter \
                  --out /tmp/test-results/rspec.xml \
                  "${TEST_FILES}"

bundler: failed to load command: rspec (/home/circleci/sbj_os/vendor/bundle/ruby/2.4.0/bin/rspec)
NoMethodError: undefined method `captures' for nil:NilClass
  /home/circleci/sbj_os/vendor/bundle/ruby/2.4.0/gems/rspec-core-3.5.4/lib/rspec/core/example.rb:124:in `parse_id'
  /home/circleci/sbj_os/vendor/bundle/ruby/2.4.0/gems/rspec-core-3.5.4/lib/rspec/core/configuration.rb:1953:in `extract_location'
  /home/circleci/sbj_os/vendor/bundle/ruby/2.4.0/gems/rspec-core-3.5.4/lib/rspec/core/configuration.rb:1892:in `block in get_files_to_run'
  /home/circleci/sbj_os/vendor/bundle/ruby/2.4.0/gems/rspec-core-3.5.4/lib/rspec/core/flat_map.rb:7:in `each'
  /home/circleci/sbj_os/vendor/bundle/ruby/2.4.0/gems/rspec-core-3.5.4/lib/rspec/core/flat_map.rb:7:in `flat_map'
  /home/circleci/sbj_os/vendor/bundle/ruby/2.4.0/gems/rspec-core-3.5.4/lib/rspec/core/flat_map.rb:7:in `flat_map'
  /home/circleci/sbj_os/vendor/bundle/ruby/2.4.0/gems/rspec-core-3.5.4/lib/rspec/core/configuration.rb:1890:in `get_files_to_run'
  /home/circleci/sbj_os/vendor/bundle/ruby/2.4.0/gems/rspec-core-3.5.4/lib/rspec/core/configuration.rb:928:in `files_to_run'
  /home/circleci/sbj_os/vendor/bundle/ruby/2.4.0/gems/rspec-core-3.5.4/lib/rspec/core/configuration.rb:1433:in `load_spec_files'
  /home/circleci/sbj_os/vendor/bundle/ruby/2.4.0/gems/rspec-core-3.5.4/lib/rspec/core/runner.rb:100:in `setup'
  /home/circleci/sbj_os/vendor/bundle/ruby/2.4.0/gems/rspec-core-3.5.4/lib/rspec/core/runner.rb:86:in `run'
  /home/circleci/sbj_os/vendor/bundle/ruby/2.4.0/gems/rspec-core-3.5.4/lib/rspec/core/runner.rb:71:in `run'
  /home/circleci/sbj_os/vendor/bundle/ruby/2.4.0/gems/rspec-core-3.5.4/lib/rspec/core/runner.rb:45:in `invoke'
  /home/circleci/sbj_os/vendor/bundle/ruby/2.4.0/gems/rspec-core-3.5.4/exe/rspec:4:in `<top (required)>'
  /home/circleci/sbj_os/vendor/bundle/ruby/2.4.0/bin/rspec:23:in `load'
  /home/circleci/sbj_os/vendor/bundle/ruby/2.4.0/bin/rspec:23:in `<top (required)>'

対応

[10/11 追記]

調べた結果 Spec fails only on CI (details included) · Issue #2451 · rspec/rspec-core に辿り着いた。コメントにある通り、下記の記述を削除することで対応した。

$(circleci tests glob "spec/**/*_spec.rb" | circleci tests split --split-by=timings)

[12/22 追記]

公式ドキュメントのサンプル Language Guide: Ruby - CircleCI が修正されていた。下記の通り、shell 実行するように書き換えることで並列実行に成功。

- - run:
-     name: run tests
-     command: |
-       mkdir /tmp/test-results
-       TEST_FILES="$(circleci tests glob "spec/**/*_spec.rb" | circleci tests split --split-by=timings)"
-       bundle exec rspec --format progress \
-                         --format RspecJunitFormatter \
-                         --out /tmp/test-results/rspec.xml
+ - type: shell
+   command: |
+     bundle exec rspec --profile 10 \
+                       --format RspecJunitFormatter \
+                       --out test_results/rspec.xml \
+                       --format progress \
+                       $(circleci tests glob "spec/**/*_spec.rb" | circleci tests split --split-by=timings)