由于与 yard gem 冲突,Sorbet `srb init` 失败并返回 Kernel#exit

Sorbet `srb init` fails with Kernel#exit due to conflict with yard gem

我们开始在工作中使用冰糕,srb init 抛出一个需要 yard 的错误。文件仍然生成并且似乎工作正常,直到 srb tc 开始产生如下错误:

sorbet/rbi/todo.rbi:38: PG::Error was previously defined as a class https://srb.help/4012
    38 |module PG::Error; end
        ^^^^^^^^^^^^^^^^
    sorbet/rbi/hidden-definitions/hidden.rbi:121092: Previous definition
      121092 |class PG::Error
              ^^^^^^^^^^^^^^^

bundle exec srb rbi hidden-definitions 重新生成隐藏定义产生了与 srb init 相同的错误,但都没有解决问题。为了确保我们的冰糕文件正确生成,似乎值得深入了解错误。

有:Ruby 2.6.5,Rails 6.1.4.1,冰糕 0.5.9115,庭院 0.9.26

重现步骤:

gem install rails
rails new srb_yard_test
cd srb_yard_test

将以下内容添加到 Gemfile

gem 'yard'
gem 'sorbet', :group => :development
gem 'sorbet-runtime'
bundle install
bundle exec srb init

来自 srb init 输出的片段:

Naming YARD::Handlers::Ruby::Legacy/Users/grahammelcher/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/yard-0.9.26/lib/yard/parser/ruby/legacy/irb/slex.rb:18: warning: already initialized constant IRB::SLex::DOUT
/Users/grahammelcher/.rbenv/versions/2.6.5/lib/ruby/2.6.0/irb/slex.rb:24: warning: previous definition of DOUT was here
/Users/grahammelcher/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/yard-0.9.26/lib/yard/parser/ruby/legacy/irb/slex.rb:19: warning: already initialized constant IRB::SLex::D_WARN
/Users/grahammelcher/.rbenv/versions/2.6.5/lib/ruby/2.6.0/irb/slex.rb:25: warning: previous definition of D_WARN was here
/Users/grahammelcher/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/yard-0.9.26/lib/yard/parser/ruby/legacy/irb/slex.rb:20: warning: already initialized constant IRB::SLex::D_DEBUG
/Users/grahammelcher/.rbenv/versions/2.6.5/lib/ruby/2.6.0/irb/slex.rb:26: warning: previous definition of D_DEBUG was here
/Users/grahammelcher/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/yard-0.9.26/lib/yard/parser/ruby/legacy/irb/slex.rb:21: warning: already initialized constant IRB::SLex::D_DETAIL
/Users/grahammelcher/.rbenv/versions/2.6.5/lib/ruby/2.6.0/irb/slex.rb:27: warning: previous definition of D_DETAIL was here
Naming YARD::Server::CommandsKernel#exit was called while requiring ruby source files
Traceback (most recent call last):
    61: from /Users/grahammelcher/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/sorbet-0.5.9115/bin/srb-rbi:237:in `<main>'
    60: from /Users/grahammelcher/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/sorbet-0.5.9115/bin/srb-rbi:196:in `main'
    59: from /Users/grahammelcher/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/sorbet-0.5.9115/bin/srb-rbi:121:in `init'
    58: from /Users/grahammelcher/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/sorbet-0.5.9115/bin/srb-rbi:232:in `block in make_step'
    57: from /Users/grahammelcher/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/sorbet-0.5.9115/lib/hidden-definition-finder.rb:38:in `main'
    56: from /Users/grahammelcher/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/sorbet-0.5.9115/lib/hidden-definition-finder.rb:44:in `main'
    55: from /Users/grahammelcher/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/sorbet-0.5.9115/lib/hidden-definition-finder.rb:75:in `all_modules_and_aliases'
    54: from /Users/grahammelcher/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/sorbet-0.5.9115/lib/hidden-definition-finder.rb:68:in `constant_cache'
    53: from /Users/grahammelcher/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/sorbet-0.5.9115/lib/gem-generator-tracepoint/tracer.rb:40:in `new'
    52: from /Users/grahammelcher/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/sorbet-0.5.9115/lib/gem-generator-tracepoint/tracer.rb:40:in `new'
    51: from /Users/grahammelcher/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/sorbet-0.5.9115/lib/constant_cache.rb:70:in `initialize'
    50: from /Users/grahammelcher/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/sorbet-0.5.9115/lib/constant_cache.rb:211:in `dfs_module'
    49: from /Users/grahammelcher/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/sorbet-0.5.9115/lib/constant_cache.rb:211:in `each'
    48: from /Users/grahammelcher/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/sorbet-0.5.9115/lib/constant_cache.rb:212:in `block in dfs_module'
    47: from /Users/grahammelcher/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/sorbet-0.5.9115/lib/constant_cache.rb:211:in `dfs_module'
    46: from /Users/grahammelcher/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/sorbet-0.5.9115/lib/constant_cache.rb:211:in `each'
    45: from /Users/grahammelcher/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/sorbet-0.5.9115/lib/constant_cache.rb:212:in `block in dfs_module'
    44: from /Users/grahammelcher/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/sorbet-0.5.9115/lib/constant_cache.rb:211:in `dfs_module'
    43: from /Users/grahammelcher/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/sorbet-0.5.9115/lib/constant_cache.rb:211:in `each'
    42: from /Users/grahammelcher/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/sorbet-0.5.9115/lib/constant_cache.rb:212:in `block in dfs_module'
    41: from /Users/grahammelcher/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/sorbet-0.5.9115/lib/constant_cache.rb:145:in `dfs_module'
    40: from /Users/grahammelcher/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/sorbet-0.5.9115/lib/constant_cache.rb:145:in `each'
    39: from /Users/grahammelcher/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/sorbet-0.5.9115/lib/constant_cache.rb:157:in `block in dfs_module'
    38: from /Users/grahammelcher/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/sorbet-0.5.9115/lib/real_stdlib.rb:77:in `real_const_get'
    37: from /Users/grahammelcher/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/sorbet-0.5.9115/lib/real_stdlib.rb:77:in `call'
    36: from /Users/grahammelcher/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/sorbet-0.5.9115/lib/real_stdlib.rb:77:in `const_get'
    35: from /Users/grahammelcher/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/zeitwerk-2.4.2/lib/zeitwerk/kernel.rb:34:in `require'
    34: from /Users/grahammelcher/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/bootsnap-1.8.1/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:31:in `require'
    33: from /Users/grahammelcher/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/bootsnap-1.8.1/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:22:in `require_with_bootsnap_lfi'
    32: from /Users/grahammelcher/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/bootsnap-1.8.1/lib/bootsnap/load_path_cache/loaded_features_index.rb:92:in `register'
    31: from /Users/grahammelcher/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/bootsnap-1.8.1/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:23:in `block in require_with_bootsnap_lfi'
    30: from /Users/grahammelcher/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/bootsnap-1.8.1/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:23:in `require'
    29: from /Users/grahammelcher/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/yard-0.9.26/lib/yard/server/commands/frames_command.rb:2:in `<main>'
    28: from /Users/grahammelcher/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/yard-0.9.26/lib/yard/server/commands/frames_command.rb:3:in `<module:YARD>'
    27: from /Users/grahammelcher/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/yard-0.9.26/lib/yard/server/commands/frames_command.rb:4:in `<module:Server>'
    26: from /Users/grahammelcher/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/yard-0.9.26/lib/yard/server/commands/frames_command.rb:6:in `<module:Commands>'
    25: from /Users/grahammelcher/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/zeitwerk-2.4.2/lib/zeitwerk/kernel.rb:34:in `require'
    24: from /Users/grahammelcher/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/bootsnap-1.8.1/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:31:in `require'
    23: from /Users/grahammelcher/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/bootsnap-1.8.1/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:22:in `require_with_bootsnap_lfi'
    22: from /Users/grahammelcher/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/bootsnap-1.8.1/lib/bootsnap/load_path_cache/loaded_features_index.rb:92:in `register'
    21: from /Users/grahammelcher/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/bootsnap-1.8.1/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:23:in `block in require_with_bootsnap_lfi'
    20: from /Users/grahammelcher/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/bootsnap-1.8.1/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:23:in `require'
    19: from /Users/grahammelcher/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/yard-0.9.26/lib/yard/server/commands/display_object_command.rb:2:in `<main>'
    18: from /Users/grahammelcher/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/yard-0.9.26/lib/yard/server/commands/display_object_command.rb:3:in `<module:YARD>'
    17: from /Users/grahammelcher/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/yard-0.9.26/lib/yard/server/commands/display_object_command.rb:4:in `<module:Server>'
    16: from /Users/grahammelcher/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/yard-0.9.26/lib/yard/server/commands/display_object_command.rb:6:in `<module:Commands>'
    15: from /Users/grahammelcher/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/zeitwerk-2.4.2/lib/zeitwerk/kernel.rb:34:in `require'
    14: from /Users/grahammelcher/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/bootsnap-1.8.1/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:31:in `require'
    13: from /Users/grahammelcher/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/bootsnap-1.8.1/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:22:in `require_with_bootsnap_lfi'
    12: from /Users/grahammelcher/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/bootsnap-1.8.1/lib/bootsnap/load_path_cache/loaded_features_index.rb:92:in `register'
    11: from /Users/grahammelcher/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/bootsnap-1.8.1/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:23:in `block in require_with_bootsnap_lfi'
    10: from /Users/grahammelcher/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/bootsnap-1.8.1/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:23:in `require'
     9: from /Users/grahammelcher/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/yard-0.9.26/lib/yard/server/commands/library_command.rb:4:in `<main>'
     8: from /Users/grahammelcher/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/yard-0.9.26/lib/yard/server/commands/library_command.rb:5:in `<module:YARD>'
     7: from /Users/grahammelcher/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/yard-0.9.26/lib/yard/server/commands/library_command.rb:6:in `<module:Server>'
     6: from /Users/grahammelcher/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/yard-0.9.26/lib/yard/server/commands/library_command.rb:32:in `<module:Commands>'
     5: from /Users/grahammelcher/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/yard-0.9.26/lib/yard/server/commands/library_command.rb:34:in `<class:LibraryCommand>'
     4: from /Users/grahammelcher/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.1.4.1/lib/active_support/fork_tracker.rb:8:in `fork'
     3: from /Users/grahammelcher/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.1.4.1/lib/active_support/fork_tracker.rb:8:in `fork'
     2: from /Users/grahammelcher/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.1.4.1/lib/active_support/fork_tracker.rb:10:in `block in fork'
     1: from /Users/grahammelcher/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/yard-0.9.26/lib/yard/server/commands/library_command.rb:34:in `block in <class:LibraryCommand>'
/Users/grahammelcher/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/sorbet-0.5.9115/lib/require_everything.rb:94:in `block in patch_kernel': ExitCalledError (ExitCalledError)

有点乱,因为YARD::Server::Commands后面少了一个换行符,所以错误其实是:

Kernel#exit was called while requiring ruby source files

有趣的是,bundle exec srb initbundle exec srb rbi hidden-definitions 的后续 运行 在与第一个 运行 略有不同的地方失败并生成略有不同的冰糕文件。第二个 运行 在 Yard::Tags 而不是 YARD::Server::Commands 处失败。 bundle exec srb rbi hidden-definitionsNaming YARD::Templates 处失败。

我已经尝试了 https://github.com/sorbet/sorbet/issues/975 的一些建议,例如在院子里使用 require: false 但同样的问题。

欢迎解决方法或建议。有一个 related issue,其中一个名为 exit 的项目在加载时以同样的方式破坏了 srb init,sorbet 团队的响应基本上是“你的代码正在调用 Kernel#exit,不要”不要那样做。

计划向 Yard and/or Sorbet 提出问题,但不清楚他们是否愿意修复它,而且我仍然不清楚院子代码中调用 #exit 的内容文件加载。

最简单的解决方案是使用 Tapioca 生成您的签名,而不是 Sorbet 的内置 rbi init/RBI 生成器系统。根据我的经验,它通常更好更快。