ruby / ruby/rubygems

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

Open
#9,586 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Bundler
Dominant language
Ruby
Stars
4k
Forks
1.9k
Avg merge
1d 2h
Merged PRs (30d)
81

Description

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:

https://github.com/ruby/rubygems/blob/884d80fd158fead18bb4f021796a2af062a45ac9/bundler/lib/bundler/installer/standalone.rb#L76

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:

https://github.com/ruby/ruby.wasm/pull/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.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start at bundler/lib/bundler/installer/standalone.rb around the referenced fallback-generation code, then inspect the generated setup.rb produced by bundle install --standalone. Reproduce the ruby.wasm state where Gem exists without the RubyGems API methods, and verify that requiring setup.rb no longer raises NoMethodError.

Written by the indexing model from the issue text.

Assessment

Tech stack
ruby, wasm
Domain
tooling
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.