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
43 changes: 29 additions & 14 deletions lib/rubygems/commands/setup_command.rb
Original file line number Diff line number Diff line change
Expand Up @@ -355,36 +355,51 @@ def install_default_bundler_gem(bin_dir)
loaded_from = current_default_spec.loaded_from
File.delete(loaded_from)

# Remove previous default gem executables if they were not shadowed by a regular gem
FileUtils.rm_rf current_default_spec.full_gem_path if all_specs_current_version.size == 1
previous_specs_dir = File.dirname(loaded_from)

# Remove previous default gem executables if they were not shadowed by a
# regular gem. They live under the same root as the previous default
# gemspec, which is not necessarily default_dir.
if all_specs_current_version.size == 1
previous_root = File.dirname(File.dirname(previous_specs_dir))
FileUtils.rm_rf File.join(previous_root, "gems", current_default_spec.full_name)
end

File.dirname(loaded_from)
previous_specs_dir
else
target_specs_dir = File.join(default_dir, "specifications", "default")
mkdir_p target_specs_dir, mode: 0o755
target_specs_dir
end

# Root directory that specs_dir belongs to. It may differ from default_dir
# when Gem.default_specifications_dir is customized to live under a
# different root, like Homebrew does. Executables must be extracted under
# the same root as the default gemspec, since that's where activation of
# the default gem will look for them.
bundler_install_dir = File.dirname(File.dirname(specs_dir))

new_bundler_spec = Gem::Specification.load("bundler.gemspec")
full_name = new_bundler_spec.full_name
gemspec_path = "#{full_name}.gemspec"

default_spec_path = File.join(specs_dir, gemspec_path)
Gem.write_binary(default_spec_path, new_bundler_spec.to_ruby)

bundler_spec = Gem::Specification.load(default_spec_path)

# Remove gemspec that was same version of vendored bundler.
normal_gemspec = File.join(default_dir, "specifications", gemspec_path)
if File.file? normal_gemspec
File.delete normal_gemspec
end

# Remove gem files that were same version of vendored bundler.
if File.directory? bundler_spec.gems_dir
Dir.entries(bundler_spec.gems_dir).
select {|default_gem| File.basename(default_gem) == full_name }.
each {|default_gem| rm_r File.join(bundler_spec.gems_dir, default_gem) }
# Remove gem files that were same version of vendored bundler. A regular
# gem lives under default_dir, which is not necessarily the same root as
# the default gemspec.
normal_gems_dir = File.join(default_dir, "gems")
if File.directory? normal_gems_dir
Dir.entries(normal_gems_dir).
select {|normal_gem| File.basename(normal_gem) == full_name }.
each {|normal_gem| rm_r File.join(normal_gems_dir, normal_gem) }
end

require_relative "../installer"
Expand All @@ -397,14 +412,14 @@ def install_default_bundler_gem(bin_dir)
format_executable: options[:format_executable],
force: options[:force],
bin_dir: bin_dir,
install_dir: default_dir,
install_dir: bundler_install_dir,
wrappers: true
)
# We need to install only executable and default spec files.
# lib/bundler.rb and lib/bundler/* are available under the site_ruby directory.
# We only need to install the executables here. The default spec was
# already written above, and lib/bundler.rb and lib/bundler/* are
# available under the site_ruby directory.
installer.extract_bin
installer.generate_bin
installer.write_default_spec
ensure
FileUtils.rm_f built_gem
end
Expand Down
27 changes: 27 additions & 0 deletions test/rubygems/test_gem_commands_setup_command.rb
Original file line number Diff line number Diff line change
Expand Up @@ -241,17 +241,44 @@ def test_install_default_bundler_gem
def test_install_default_bundler_gem_with_default_gems_not_installed_at_default_dir
@cmd.extend FileUtils

# Simulate Homebrew's layout, where Gem.default_dir is moved to a
# different root, while Gem.default_specifications_dir stays inside the
# ruby install tree.
gemhome2 = File.join(@tempdir, "gemhome2")
Gem.instance_variable_set(:@default_dir, gemhome2)

FileUtils.mkdir_p gemhome2
bin_dir = File.join(gemhome2, "bin")

previous_default_gem_dir = File.join(@gemhome, "gems", "bundler-1.15.4")
write_file File.join(previous_default_gem_dir, "exe", "bundle")

# A regular gem of the same version installed at the default dir, which is
# promoted to the default gem and must be removed. Its files live under
# default_dir, not under the default gemspec root.
normal_gem_dir = File.join(gemhome2, "gems", "bundler-#{bundler_version}")
write_file File.join(normal_gem_dir, "lib", "bundler.rb")

@cmd.install_default_bundler_gem bin_dir

# expect to remove other versions of bundler gemspecs on default specification directory.
assert_path_not_exist previous_bundler_specification_path
assert_path_exist new_bundler_specification_path

# expect to remove the previous default version executables, which live
# next to the removed gemspec, not under the new default dir.
assert_path_not_exist previous_default_gem_dir

# expect to remove the promoted regular gem's files under default_dir.
assert_path_not_exist normal_gem_dir

# expect executables to be installed under the same root as the default
# gemspec, since that's where activation of the default gem looks for them.
spec = Gem::Specification.load(new_bundler_specification_path)
spec.executables.each do |e|
assert_path_exist File.join(@gemhome, "gems", spec.full_name, spec.bindir, e)
end
assert_path_not_exist File.join(gemhome2, "gems", spec.full_name)
end

def test_install_default_bundler_gem_with_force_flag
Expand Down