Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/system-rubygems-bundler.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ jobs:
- name: Run Test
run: |
bin/parallel_rspec
env:
RGV: system
- name: Save system RubyGems version to ENV
run: |
RGV=$(ruby -e 'puts Gem::VERSION.split(".")[0..2].join(".")')
Expand Down
8 changes: 6 additions & 2 deletions doc/GETTING_STARTED.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,14 +73,18 @@ For realworld higher level specs (also run in CI):

## Developing Bundler and RubyGems Together

When developing Bundler features or bug fixes that require changes in RubyGems, you can set the `RGV` environment variable to point to the repository root so Bundler's test suite picks up those changes:
Bundler's test suite runs against the RubyGems version selected by the `RGV` environment variable. It defaults to `.`, the RubyGems in this repository, so no setup is needed for the usual case of developing Bundler and RubyGems together:

RGV=.. bin/parallel_rspec
bin/parallel_rspec

You can also test against specific RubyGems versions:

RGV=v3.2.33 bin/parallel_rspec

Or against the RubyGems copy shipped with your Ruby, as the system-rubygems-bundler CI job does:

RGV=system bin/parallel_rspec

It's recommended to set this variable permanently using [direnv](https://direnv.net) for consistent development.

## Code Style and Quality
Expand Down
9 changes: 9 additions & 0 deletions lib/bundler/rubygems_gem_installer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,15 @@ def generate_plugins
end
end

# Reimplemented from RubyGems, since RubyGems older than 4.1 doesn't
# provide it
def remove_stale_plugins
return unless spec.plugins.empty?

ensure_writable_dir @plugins_dir
remove_plugins_for(spec, @plugins_dir)
end

def warn_skipped_extensions
return if spec.extensions.empty?

Expand Down
23 changes: 18 additions & 5 deletions lib/bundler/vendored_tsort.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,21 @@
# frozen_string_literal: true

begin
require "rubygems/vendored_tsort"
rescue LoadError
require "tsort"
Gem::TSort = TSort
# The defined? guard avoids reopening Gem::TSort when an old RubyGems has
# already loaded its own copy, e.g. through rubygems/request_set from
# Gem.activate_bin_path in binstubs.
#
unless defined?(Gem::TSort)
begin
require "rubygems/vendored_tsort"
rescue LoadError
begin
# RubyGems 3.4 and 3.5 ship the same file under its pre-3.6 name.
# Requiring the real tsort here instead would activate the tsort
# default gem, and `bundler/setup` must not activate any gems.
require "rubygems/tsort"
rescue LoadError
require "tsort"
Gem::TSort = TSort
end
end
end
4 changes: 2 additions & 2 deletions spec/commands/exec_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -75,13 +75,13 @@
skip "https://github.com/ruby/rubygems/issues/3351" if mswin?
install_gemfile "source \"https://gem.repo1\"; gem \"myrack\""
bundle "exec #{gem_cmd} --version"
expect(out).to eq(Gem::VERSION)
expect(out).to eq(exercised_rubygems_version.to_s)
end

it "works when exec'ing to rubygems through sh -c" do
install_gemfile "source \"https://gem.repo1\"; gem \"myrack\""
bundle "exec sh -c '#{gem_cmd} --version'"
expect(out).to eq(Gem::VERSION)
expect(out).to eq(exercised_rubygems_version.to_s)
end

it "works when exec'ing back to bundler to run a remote resolve" do
Expand Down
4 changes: 2 additions & 2 deletions spec/commands/install_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1394,7 +1394,7 @@ def run
expect(gem_make_out).not_to include("make -j8")
end

it "uses 3 slots from the available pool when running the compilation of an extension" do
it "uses 3 slots from the available pool when running the compilation of an extension", rubygems: ">= 4.1.0.dev" do
ENV.delete("MAKEFLAGS")

install_gemfile(<<~G, env: { "BUNDLE_JOBS" => "8" })
Expand All @@ -1407,7 +1407,7 @@ def run
expect(gem_make_out).to include("make -j3")
end

it "consumes 3 slots from the pool when BUNDLE_JOBS isn't set" do
it "consumes 3 slots from the pool when BUNDLE_JOBS isn't set", rubygems: ">= 4.1.0.dev" do
ENV.delete("MAKEFLAGS")

install_gemfile(<<~G)
Expand Down
7 changes: 5 additions & 2 deletions spec/install/gemfile/git_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -114,9 +114,12 @@
sha = git.ref_for("main", 11)
spec_file = default_bundle_path("bundler/gems/foo-1.0-#{sha}/foo.gemspec")
expect(spec_file).to exist
ruby_code = Gem::Specification.load(spec_file.to_s).to_ruby
# Serialize with the RubyGems that wrote the file, since `#to_ruby`
# output differs across RubyGems versions
ruby "print Gem::Specification.load(#{spec_file.to_s.dump}).to_ruby"
ruby_code = out
file_code = File.read(spec_file)
expect(file_code).to eq(ruby_code)
expect(file_code.strip).to eq(ruby_code)
end

it "does not update the git source implicitly" do
Expand Down
2 changes: 1 addition & 1 deletion spec/install/gems/resolving_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -726,7 +726,7 @@
Because every version of require_rubygems depends on RubyGems > 9000
and Gemfile depends on require_rubygems >= 0,
RubyGems > 9000 is required.
So, because current RubyGems version is = #{Gem::VERSION},
So, because current RubyGems version is = #{exercised_rubygems_version},
version solving has failed.
E
expect(err).to end_with(nice_error)
Expand Down
7 changes: 4 additions & 3 deletions spec/install/global_cache_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@
let(:source2) { "http://gemserver.example.org" }

def cache_base
# Use the unified global gem cache path if available (from RubyGems),
# otherwise fall back to the Bundler-specific cache location
if Gem.respond_to?(:global_gem_cache_path)
# Use the unified global gem cache path if the RubyGems under test
# provides it, otherwise fall back to the Bundler-specific cache
# location that Bundler uses on RubyGems older than 4.0
if exercised_rubygems_version >= Gem::Version.new("4.0.0.a")
Pathname.new(Gem.global_gem_cache_path)
else
home(".bundle", "cache", "gems")
Expand Down
8 changes: 3 additions & 5 deletions spec/runtime/self_management_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -171,11 +171,9 @@
expect(out).to eq(previous_minor)
end

it "requires the right bundler version from the config and run bundle CLI without re-exec" do
unless Bundler.rubygems.provides?(">= 4.1.0.dev")
skip "This spec can only run when Gem::BundlerVersionFinder.bundler_versions reads bundler configs"
end

# Can only run when Gem::BundlerVersionFinder.bundler_versions reads
# bundler configs
it "requires the right bundler version from the config and run bundle CLI without re-exec", rubygems: ">= 4.1.0.dev" do
lockfile_bundled_with(current_version)

bundle_config "version #{previous_minor}"
Expand Down
5 changes: 4 additions & 1 deletion spec/support/filters.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@ def inspect
RSpec.configure do |config|
config.filter_run_excluding realworld: true

config.filter_run_excluding rubygems: RequirementChecker.against(Gem.rubygems_version)
# Version-gated specs care about the RubyGems that spec-spawned processes
# run, which under RGV=system is older than the one loaded in this process.
exercised_rubygems_version = Gem::Version.new(ENV["BUNDLER_SPEC_SYSTEM_RUBYGEMS_VERSION"] || Gem::VERSION)
config.filter_run_excluding rubygems: RequirementChecker.against(exercised_rubygems_version)
config.filter_run_excluding git: RequirementChecker.against(git_version)
config.filter_run_excluding ruby_repo: !ENV["GEM_COMMAND"].nil?
config.filter_run_excluding no_color_tty: Gem.win_platform? || !ENV["GITHUB_ACTION"].nil?
Expand Down
15 changes: 14 additions & 1 deletion spec/support/helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -183,9 +183,22 @@ def gembin(cmd, options = {})
sys_exec(cmd.to_s, options)
end

# The RubyGems version that processes spawned by specs run. Under
# RGV=system it's the system RubyGems, which is older than the one loaded
# in the current process.
def exercised_rubygems_version
@exercised_rubygems_version ||= Gem::Version.new(ENV["BUNDLER_SPEC_SYSTEM_RUBYGEMS_VERSION"] || Gem::VERSION)
end

def sys_exec(cmd, options = {}, &block)
env = options[:env] || {}
env["RUBYOPT"] = opt_add(opt_add("-r#{spec_dir}/support/switch_rubygems.rb", env["RUBYOPT"]), ENV["RUBYOPT"])
if ENV["RGV"] == "system"
# Spawned processes must boot system RubyGems untouched, so don't make
# them switch, and drop the load path override the harness runs with.
env["RUBYOPT"] = opt_add(env["RUBYOPT"] || "", opt_remove("-I#{source_lib_dir}", ENV["RUBYOPT"]))
else
env["RUBYOPT"] = opt_add(opt_add("-r#{spec_dir}/support/switch_rubygems.rb", env["RUBYOPT"]), ENV["RUBYOPT"])
end
options[:env] = env

sh(cmd, options, &block)
Expand Down
15 changes: 14 additions & 1 deletion spec/support/switch_rubygems.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,17 @@

require_relative "rubygems_version_manager"
ENV["RGV"] ||= "."
RubygemsVersionManager.new(ENV["RGV"]).switch

# RGV=system runs specs against system RubyGems: processes spawned by specs
# boot it untouched (see `Spec::Helpers#sys_exec`). The test harness itself
# still runs on the checked out RubyGems, because the repo's lib/ dir is
# unavoidably on its load path and would get mixed into an older RubyGems.
# The system RubyGems version is captured here, while it's still the one
# loaded, so version-gated specs can check the version actually under test.
source = ENV["RGV"]
if source == "system"
ENV["BUNDLER_SPEC_SYSTEM_RUBYGEMS_VERSION"] ||= Gem::VERSION
source = "."
end

RubygemsVersionManager.new(source).switch