🔥

RuboCop RSpecでrake実行時にrake aborted! default.yml is out of syncが出たときの対応

2021/05/16に公開

はじめに

RuboCop RSpecプロジェクトで、コードを修正してrakeを走らせたときに少し詰まってしまったので、備忘録として残しておきます。
なお、この記録は2021/05/16時点の情報となります。

条件

  • プロジェクト:

https://github.com/rubocop/rubocop-rspec

  • 修正ファイル
    • config/default.yml
    • libフォルダ以下のファイル
    • specフォルダ以下のファイル
  • 修正ファイルはすべてunstageのまま

現象

bundle exec rakeを実行すると、rake aborted! default.yml is out of syncとなってしまいました。

$ bundle exec rake
・・・略・・・
You can opt out of this message by adding the following to your config (see https://docs.rubocop.org/rubocop/extensions.html#extension-suggestions for more options):
  AllCops:
    SuggestExtensions: false
rake aborted!
default.yml is out of sync:

diff --git a/config/default.yml b/config/default.yml
index 1b3b7d3..c4fb381 100644
--- a/config/default.yml
+++ b/config/default.yml
@@ -469,10 +469,12 @@ RSpec/MissingExampleGroupArgument:
   StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/MissingExampleGroupArgument
 
 RSpec/MultipleDescribes:
-  Description: Checks for multiple top-level example groups.
+  Description: Checks for multiple top-level example groups or nested example groups.
   Enabled: true
   VersionAdded: '1.0'
+  VersionChanged: '2.4'
   StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/MultipleDescribes
+  Splitting: false
 
 RSpec/MultipleExpectations:
   Description: Checks if examples contain too many `expect` calls.

Run bin/build_config
/Users/xxx/rubocop-rspec/Rakefile:45:in `block in <top (required)>'
/Users/xxx/rubocop-rspec/vendor/bundle/ruby/2.7.0/gems/rake-13.0.3/exe/rake:27:in `<top (required)>'
/Users/yyy/.rbenv/versions/2.7.0/bin/bundle:23:in `load'
/Users/yyy/.rbenv/versions/2.7.0/bin/bundle:23:in `<main>'
Tasks: TOP => default => confirm_config
(See full trace by running task with --trace)
$

原因

結論

gitでconfig/default.ymlをaddしていなかっただけです。

調査

エラーとして記載されているRakefileの該当箇所を見てみると、

desc 'Confirm config/default.yml is up to date'
task confirm_config: :build_config do
  _, stdout, _, process =
    Open3.popen3('git diff --exit-code config/default.yml')

  raise <<~ERROR unless process.value.success?
    default.yml is out of sync:

    #{stdout.read}
    Run bin/build_config
  ERROR
end

https://github.com/rubocop/rubocop-rspec/blob/721fd6c0651153aa76ee6c91fd836d95ee4d37e0/Rakefile#L40

とありました。
エラーメッセージが出る理由は、unless process.value.success?falseのためのようです。
では、なぜfalseとなるのか?
1つずつたどっていきます。

git diff --exit-code config/default.ymlのコマンドについて

git diff --exit-codeの説明を公式サイトで確認します。
https://git-scm.com/docs/git-diff#Documentation/git-diff.txt---exit-code

Make the program exit with codes similar to diff(1). That is, it exits with 1 if there were differences and 0 means no differences.

この説明と、条件項目でconfig/default.ymlはunsatgeの状態であったことから、差分が残っているとなり、exit codeが1となります。

Open3.popen3について

processを返しているOpen3.popen3を見てみます。

  def popen3(*cmd, &block)
    if Hash === cmd.last
      opts = cmd.pop.dup
    else
      opts = {}
    end

    in_r, in_w = IO.pipe
    opts[:in] = in_r
    in_w.sync = true

    out_r, out_w = IO.pipe
    opts[:out] = out_w

    err_r, err_w = IO.pipe
    opts[:err] = err_w

    popen_run(cmd, opts, [in_r, out_w, err_w], [in_w, out_r, err_r], &block)
  end

https://github.com/ruby/open3/blob/fd28f90a1452b6bbc8d10ce57f807d7d37000c3b/lib/open3.rb#L85

popen3メソッドではpopen_runメソッドを呼び出しているので、そのコードを見てみます。

  def popen_run(cmd, opts, child_io, parent_io) # :nodoc:
    pid = spawn(*cmd, opts)
    wait_thr = Process.detach(pid)
    child_io.each(&:close)
    result = [*parent_io, wait_thr]
    if defined? yield
      begin
        return yield(*result)
      ensure
        parent_io.each(&:close)
        wait_thr.join
      end
    end
    result
  end

https://github.com/ruby/open3/blob/fd28f90a1452b6bbc8d10ce57f807d7d37000c3b/lib/open3.rb#L220

spawnで実行コマンドのプロセスIDを返し、Process.detachで終了ステータスをProcess::Statusとしてwait_thrにセットし、返しているようです。
https://docs.ruby-lang.org/ja/latest/method/Process/m/detach.html

よってgit diff --exit-codeのコマンドの調査と組み合わせると、今回は終了コードが1だったので、successとならず、今回の現象が起きたと思われます。

対応

git addしてbundle exec rakeを再実行しました。
その結果、abortedすることなく完了しました。

$ bundle exec rake
・・・略・・・
You can opt out of this message by adding the following to your config (see https://docs.rubocop.org/rubocop/extensions.html#extension-suggestions for more options):
  AllCops:
    SuggestExtensions: false
Files:          92
Modules:        14 (    6 undocumented)
Classes:        94 (    0 undocumented)
Constants:     144 (  143 undocumented)
Attributes:      1 (    0 undocumented)
Methods:       277 (  242 undocumented)
 26.23% documented
* generated /Users/xxx/rubocop-rspec/docs/modules/ROOT/pages/cops_rspec.adoc
* generated /Users/xxx/rubocop-rspec/docs/modules/ROOT/pages/cops_rspec/capybara.adoc
* generated /Users/xxx/rubocop-rspec/docs/modules/ROOT/pages/cops_rspec/factorybot.adoc
* generated /Users/xxx/rubocop-rspec/docs/modules/ROOT/pages/cops_rspec/rails.adoc
$

終わりに

最初にエラーを見たときは、rake実行時のエラーなのになぜout of syncなのだろうと疑問に思っていました。
実際に蓋を開けてみたところ単純な原因でしたが、なぜエラーになるのかと1つずつ紐解いて経験を積むことができたので良かったと思います。
この記事が誰かのお役に立てれば幸いです。

Discussion