Skip to content

Bundler standalone setup assumes defined?(Gem) implies Gem.ruby_api_version is available #9586

@ledsun

Description

@ledsun

Describe the problem as clearly as you can

Bundler standalone setup assumes that if the Gem constant is defined, RubyGems API helpers such as Gem.ruby_api_version and Gem.extension_api_version are also available.

This assumption can fail in ruby.wasm.

In ruby.wasm, after Ruby VM initialization, we can observe a state where Gem is already defined, but RubyGems has not been fully loaded yet. In that state:

defined?(Gem) #=> "constant"
Gem.respond_to?(:ruby_api_version) #=> false
Gem.respond_to?(:extension_api_version) #=> false

Bundler standalone setup generated by bundle install --standalone only checks unless defined?(Gem) before defining fallback helpers:

So, when Gem is partially defined, the fallback helpers are not defined, but the generated load paths still use Gem.ruby_api_version and Gem.extension_api_version. This can fail with NoMethodError before application code starts.

This is currently known as a ruby.wasm-specific issue. I do not think this affects normal CRuby usage, where VM initialization appears to leave Gem and RubyGems in a consistent state.

ruby.wasm has worked around this by requiring RubyGems before loading the generated standalone setup when Gem is defined but Gem.ruby_api_version is missing:

ruby/ruby.wasm#622

I would like to ask whether Bundler standalone setup should be more defensive here, for example by checking whether the specific methods it needs are available, instead of checking only defined?(Gem).

Did you try upgrading rubygems & bundler?

Yes.

I checked the current Bundler standalone implementation on the rubygems main branch, and the generated fallback still appears to use only unless defined?(Gem):

https://github.com/ruby/rubygems/blob/master/bundler/lib/bundler/installer/standalone.rb

So I believe this behavior is still present.

Post steps to reproduce the problem

This is not known to reproduce on normal CRuby. It is observed in ruby.wasm, where the Ruby VM can be initialized with Gem defined but RubyGems not fully loaded.

Which command did you run?

In ruby.wasm packaging, Bundler is invoked with standalone mode, roughly:

bundle install --standalone --target-rbconfig path/to/target/rbconfig.rb

Then, at runtime, ruby.wasm loads the generated setup file:

require "/bundle/bundler/setup.rb"

What were you expecting to happen?

I expected the generated standalone setup.rb not to fail just because the Gem constant already exists.

More specifically, if setup.rb needs Gem.ruby_api_version and Gem.extension_api_version, I expected it to either:

  • define fallback versions of those methods when they are missing, or
  • clearly rely on RubyGems being fully loaded before setup.rb is required.

If Bundler expects the second behavior, then ruby.wasm should probably require RubyGems before loading the generated standalone setup file. I would like to confirm which behavior Bundler expects.

What happened instead?

The generated standalone setup can fail with NoMethodError because Gem.ruby_api_version or Gem.extension_api_version is missing.

NoMethodError: undefined method `ruby_api_version' for module Gem

If not included with the output of your command, run bundle env and paste the output below

This is observed in ruby.wasm rather than a normal local CRuby environment, so bundle env from the host environment may not represent the runtime where the failure occurs.

Relevant context:

This may be related to #7545, which also describes a standalone setup case where Gem exists but RubyGems is not fully available. The missing method is different there (Gem.try_activate), while this issue is about Gem.ruby_api_version and Gem.extension_api_version used by the generated standalone load paths.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions