🔗 概要
以下のように、default gemsであるRubyのopensslをgem update opensslコマンドで更新したとします。
一方、bundlerを使うRubyアプリ(ここではRails)ではopensslをインストールしていないとします。
$ gem list|rg openssl
openssl (3.3.2, default: 3.3.0)
$ bundle list|rg openssl
# 表示なし
この場合、bundlerを使うRubyアプリでは新しいopenssl 3.3.2が使われると思いきや、何とデフォルトの3.3.0が使われるのです。
このような状態でRailsでopenssl 3.3.2を使うには、RailsのGemfileで明示的にopensslを追加してbundle installする必要があります↓。
# Gemfile
gem "openssl"
なお、どのgemがdefault gemもしくはbundled gemかについては以下で確認できます。
参考: Standard Gems 3.4.7
参考: standard librariesとdefault gemsとbundled gemsの違い - ESM アジャイル事業部 開発者ブログ
🔗 背景と経緯
この振る舞いは、以下のトラブルシューティングで知りました。
bin/importmap pinコマンドを実行したところ、以下のエラーが発生しました。
$ bin/importmap pin turbo-transition
/Users/hachi8833/.anyenv/envs/rbenv/versions/3.4.7/lib/ruby/gems/3.4.0/gems/importmap-rails-2.2.2/lib/importmap/packager.rb:133:in 'Importmap::Packager#post_json': Unexpected transport error (OpenSSL::SSL::SSLError: SSL_connect returned=1 errno=0 peeraddr=[2001:4860:4802:36::15]:443 state=error: certificate verify failed (unable to get certificate CRL)) (Importmap::Packager::HTTPError)
from /Users/hachi8833/.anyenv/envs/rbenv/versions/3.4.7/lib/ruby/gems/3.4.0/gems/importmap-rails-2.2.2/lib/importmap/packager.rb:24:in 'Importmap::Packager#import'
from /Users/hachi8833/.anyenv/envs/rbenv/versions/3.4.7/lib/ruby/gems/3.4.0/gems/importmap-rails-2.2.2/lib/importmap/commands.rb:180:in 'Importmap::Commands#for_each_import'
from /Users/hachi8833/.anyenv/envs/rbenv/versions/3.4.7/lib/ruby/gems/3.4.0/gems/importmap-rails-2.2.2/lib/importmap/commands.rb:17:in 'Importmap::Commands#pin'
from /Users/hachi8833/.anyenv/envs/rbenv/versions/3.4.7/lib/ruby/gems/3.4.0/gems/thor-1.4.0/lib/thor/command.rb:28:in 'Thor::Command#run'
from /Users/hachi8833/.anyenv/envs/rbenv/versions/3.4.7/lib/ruby/gems/3.4.0/gems/thor-1.4.0/lib/thor/invocation.rb:127:in 'Thor::Invocation#invoke_command'
from /Users/hachi8833/.anyenv/envs/rbenv/versions/3.4.7/lib/ruby/gems/3.4.0/gems/thor-1.4.0/lib/thor.rb:538:in 'Thor.dispatch'
from /Users/hachi8833/.anyenv/envs/rbenv/versions/3.4.7/lib/ruby/gems/3.4.0/gems/thor-1.4.0/lib/thor/base.rb:584:in 'Thor::Base::ClassMethods#start'
from /Users/hachi8833/.anyenv/envs/rbenv/versions/3.4.7/lib/ruby/gems/3.4.0/gems/importmap-rails-2.2.2/lib/importmap/commands.rb:190:in '<main>'
from /Users/hachi8833/.anyenv/envs/rbenv/versions/3.4.7/lib/ruby/3.4.0/bundled_gems.rb:82:in 'Kernel.require'
from /Users/hachi8833/.anyenv/envs/rbenv/versions/3.4.7/lib/ruby/3.4.0/bundled_gems.rb:82:in 'block (2 levels) in Kernel#replace_require'
from /Users/hachi8833/.anyenv/envs/rbenv/versions/3.4.7/lib/ruby/gems/3.4.0/gems/bootsnap-1.18.6/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:30:in 'Kernel#require'
from /Users/hachi8833/.anyenv/envs/rbenv/versions/3.4.7/lib/ruby/gems/3.4.0/gems/zeitwerk-2.7.3/lib/zeitwerk/core_ext/kernel.rb:34:in 'Kernel#require'
from bin/importmap:4:in '<main>'
/Users/hachi8833/.anyenv/envs/rbenv/versions/3.4.7/lib/ruby/gems/3.4.0/gems/net-protocol-0.2.2/lib/net/protocol.rb:46:in 'OpenSSL::SSL::SSLSocket#connect_nonblock': SSL_connect returned=1 errno=0 peeraddr=[2001:4860:4802:36::15]:443 state=error: certificate verify failed (unable to get certificate CRL) (OpenSSL::SSL::SSLError)
from /Users/hachi8833/.anyenv/envs/rbenv/versions/3.4.7/lib/ruby/gems/3.4.0/gems/net-protocol-0.2.2/lib/net/protocol.rb:46:in 'Net::Protocol#ssl_socket_connect'
from /Users/hachi8833/.anyenv/envs/rbenv/versions/3.4.7/lib/ruby/3.4.0/net/http.rb:1736:in 'Net::HTTP#connect'
from /Users/hachi8833/.anyenv/envs/rbenv/versions/3.4.7/lib/ruby/3.4.0/net/http.rb:1636:in 'Net::HTTP#do_start'
from /Users/hachi8833/.anyenv/envs/rbenv/versions/3.4.7/lib/ruby/3.4.0/net/http.rb:1625:in 'Net::HTTP#start'
from /Users/hachi8833/.anyenv/envs/rbenv/versions/3.4.7/lib/ruby/3.4.0/net/http.rb:1064:in 'Net::HTTP.start'
from /Users/hachi8833/.anyenv/envs/rbenv/versions/3.4.7/lib/ruby/3.4.0/net/http.rb:858:in 'Net::HTTP.post'
from /Users/hachi8833/.anyenv/envs/rbenv/versions/3.4.7/lib/ruby/gems/3.4.0/gems/importmap-rails-2.2.2/lib/importmap/packager.rb:131:in 'Importmap::Packager#post_json'
from /Users/hachi8833/.anyenv/envs/rbenv/versions/3.4.7/lib/ruby/gems/3.4.0/gems/importmap-rails-2.2.2/lib/importmap/packager.rb:24:in 'Importmap::Packager#import'
from /Users/hachi8833/.anyenv/envs/rbenv/versions/3.4.7/lib/ruby/gems/3.4.0/gems/importmap-rails-2.2.2/lib/importmap/commands.rb:180:in 'Importmap::Commands#for_each_import'
from /Users/hachi8833/.anyenv/envs/rbenv/versions/3.4.7/lib/ruby/gems/3.4.0/gems/importmap-rails-2.2.2/lib/importmap/commands.rb:17:in 'Importmap::Commands#pin'
from /Users/hachi8833/.anyenv/envs/rbenv/versions/3.4.7/lib/ruby/gems/3.4.0/gems/thor-1.4.0/lib/thor/command.rb:28:in 'Thor::Command#run'
from /Users/hachi8833/.anyenv/envs/rbenv/versions/3.4.7/lib/ruby/gems/3.4.0/gems/thor-1.4.0/lib/thor/invocation.rb:127:in 'Thor::Invocation#invoke_command'
from /Users/hachi8833/.anyenv/envs/rbenv/versions/3.4.7/lib/ruby/gems/3.4.0/gems/thor-1.4.0/lib/thor.rb:538:in 'Thor.dispatch'
from /Users/hachi8833/.anyenv/envs/rbenv/versions/3.4.7/lib/ruby/gems/3.4.0/gems/thor-1.4.0/lib/thor/base.rb:584:in 'Thor::Base::ClassMethods#start'
from /Users/hachi8833/.anyenv/envs/rbenv/versions/3.4.7/lib/ruby/gems/3.4.0/gems/importmap-rails-2.2.2/lib/importmap/commands.rb:190:in '<main>'
from /Users/hachi8833/.anyenv/envs/rbenv/versions/3.4.7/lib/ruby/3.4.0/bundled_gems.rb:82:in 'Kernel.require'
from /Users/hachi8833/.anyenv/envs/rbenv/versions/3.4.7/lib/ruby/3.4.0/bundled_gems.rb:82:in 'block (2 levels) in Kernel#replace_require'
from /Users/hachi8833/.anyenv/envs/rbenv/versions/3.4.7/lib/ruby/gems/3.4.0/gems/bootsnap-1.18.6/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:30:in 'Kernel#require'
from /Users/hachi8833/.anyenv/envs/rbenv/versions/3.4.7/lib/ruby/gems/3.4.0/gems/zeitwerk-2.7.3/lib/zeitwerk/core_ext/kernel.rb:34:in 'Kernel#require'
from bin/importmap:4:in '<main>'
Railsで以下のissueを見つけました。
まず、本家OpenSSL 3.xのCライブラリ(Rubyのopenssl gemのことではありません)にあった問題が3.6.0で修正されました。
Rubyのopenssl gem v3.3.0はその修正を反映していなかったため、importmapで上述のエラーが発生します。その後v3.3.2で本家3.6.0の修正に対応しました(#957)。
🔗 症状
冒頭の状況を再録します。
$ gem list|rg openssl
openssl (3.3.2, default: 3.3.0)
$ bundle list|rg openssl
# 表示なし
以上の経緯から、gemコマンドでopenssl gemを3.3.2にアップグレードすれば、importmapのOpenSSLエラーを修正できるだろうと考えました。
しかしgem update opensslコマンドを実行してもまったく問題が解消されず、慌てました。
おかしいと思って、Ruby 3.4.7をリビルトしたり、gemコマンドで全部のdefault gemsなどを取得し直したりしたのですが、一向に解決できません。
🔗 解決方法
ふと、issue #55886のコメントにこんなことが書いてありました。
そこでGemfileにopenssl gemを追加してbundle installしたところ、あっさり問題が解消しました。
# Gemfile
gem "openssl"
# bundle install実行後
$ gem list|rg openssl
openssl (3.3.2, default: 3.3.0)
$ bundle list|rg openssl
* openssl (3.3.2)
$ bin/importmap pin turbo-transition
Pinning "turbo-transition" to vendor/javascript/turbo-transition.js via download from https://ga.jspm.io/npm:turbo-transition@0.4.0/dist/turbo-transition.esm.js
なお、Ruby本体の新しいリリースでdefault gemsが更新されれば、rbenvなりasfdなりmiseなりで新しいRubyをインストール/ビルドし、そのRubyを使うように環境を切り替えれば、この対処は不要になります。
🔗 補足
冒頭に書いたように、gem update opensslを実行しても、もともとあったdefault gemとしてのopensslは置き換えられず、更新版のopensslが追加されるだけです。
しかしこのことは、意外にもbundlerのドキュメントには見当たりませんでした↓。
参考: Bundler: The best way to manage a Ruby application's gems
ChatGPTに相談してみたところ、bundlerのadd_default_gems_toメソッド↓ではdefault gemを取得するときに、Rubyに同梱されているオリジナルのバージョンしか取得していないと説明してもらいました。
# rubygems/bundler/lib/bundler/rubygems_integration.rb#280
def add_default_gems_to(specs)
specs_by_name = specs.reduce({}) do |h, s|
h[s.name] = s
h
end
Bundler.rubygems.default_stubs.each do |stub|
default_spec = stub.to_spec
default_spec_name = default_spec.name
next if specs_by_name.key?(default_spec_name)
specs_by_name[default_spec_name] = default_spec
end
specs_by_name
end
Bundlerは
Gem::Specification.default_stubsを参照しますが、これはRubyが ビルドした時点で同梱したdefault gemのバージョンだけを返します。gem updateで追加されたopenssl(3.3.2)はこのリストには入りません。
そもそもbundler単体というよりはrubygemと組み合わさったときの挙動なので、ドキュメントでそこまでカバーしていなくてもしょうがないかもしれません。
解消して欲しい気もしますが、いかにもどこかで互換性が壊れそうなので、今後類似の問題が起きたらGemfileに明示的に追加することにします。
なお、ChatGPTが以下のアドバイスもくれました。
同じパターンは date, rexml, net-http など他の default gem でも起こりうるので、挙動が怪しいときは一度 Gemfile に明示してみると原因切り分けがしやすくなります。

環境