Skip to content

Commit

Permalink
RUBY-2322 allow bundler to heal itself when mismatching versions in play
Browse files Browse the repository at this point in the history
  • Loading branch information
Michael Lang committed Mar 18, 2020
1 parent 9d5c24a commit 8e58861
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 16 deletions.
25 changes: 25 additions & 0 deletions lib/tasks/multiverse.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,36 @@

namespace :multiverse do

def remove_local_multiverse_databases
list_databases_command = %{echo "show databases" | mysql -u root}
databases = `#{list_databases_command}`.chomp!.split("\n").select{|s| s =~ /multiverse/}
databases.each do |database|
puts "Dropping #{database}"
`echo "drop database #{database}" | mysql -u root`
end
rescue => error
puts "ERROR: Cannot get MySQL databases..."
puts error.message
end

def remove_generated_gemfiles
file_path = File.expand_path "test/multiverse/suites"
Dir.glob(File.join file_path, "**", "Gemfile*").each do |fn|
puts "Removing #{fn.gsub(file_path,'.../suites')}"
FileUtils.rm fn
end
end

task :env do
# ENV['SUITES_DIRECTORY'] = File.expand_path('../../test/multiverse/suites', __FILE__)
require File.expand_path('../../../test/multiverse/lib/multiverse', __FILE__)
end

task :clobber do
remove_local_multiverse_databases
remove_generated_gemfiles
end

desc "Clean cached gemfiles from Bundler.bundle_path"
task :clean_gemfile_cache do
glob = File.expand_path 'multiverse-cache/Gemfile.*.lock', Bundler.bundle_path
Expand Down
100 changes: 84 additions & 16 deletions test/multiverse/lib/multiverse/suite.rb
Original file line number Diff line number Diff line change
Expand Up @@ -91,21 +91,81 @@ def load_dependencies(gemfile_text, env_index, should_print=true)
generate_gemfile(gemfile_text, env_index)
ensure_bundle(env_index)
rescue => e
puts "#{e.class}: #{e}"
puts "Fast local bundle failed. Attempting to install from rubygems.org"
if verbose?
puts "#{e.class}: #{e}"
puts e.backtrace
puts "Fast local bundle failed. Attempting to install from rubygems.org"
end
clean_gemfiles(env_index)
generate_gemfile(gemfile_text, env_index, false)
ensure_bundle(env_index)
end
print_environment if should_print
end

def with_potentially_mismatched_bundler
yield
rescue Bundler::LockfileError => error
raise if @retried
if verbose?
puts "Encountered Bundler error: #{error.message}"
puts "Currently Active Bundler Version: #{Bundler::VERSION}"
end
change_lock_version(`pwd`, ENV["BUNDLE_GEMFILE"])
@retried = true
retry
end

def bundling_lock_file
File.join(Bundler.bundle_path, 'multiverse-bundler.lock')
with_potentially_mismatched_bundler do
File.join(Bundler.bundle_path, 'multiverse-bundler.lock')
end
end

def bundler_cache_dir
File.join(Bundler.bundle_path, 'multiverse-cache')
with_potentially_mismatched_bundler do
File.join(Bundler.bundle_path, 'multiverse-cache')
end
end

def explicit_bundler_version dir
return if RUBY_VERSION.to_f < 2.3
fn = File.join(dir, ".bundler-version")
version = File.exist?(fn) ? File.read(fn).chomp.to_s.strip : nil
version.to_s == "" ? nil : "_#{version}_"
end

def bundle_install(dir, exact_version=nil)
puts "Bundling in #{dir}..."
bundler_version = exact_version || explicit_bundler_version(dir)
bundle_cmd = "bundle #{explicit_bundler_version(dir)}".strip
full_bundle_cmd = "#{bundle_cmd} install --retry 3 --jobs 4"
result = ShellUtils.try_command_n_times full_bundle_cmd, 3
unless $?.success?
puts "Failed local bundle, trying again without the version lock..."
change_lock_version(dir, ENV["BUNDLE_GEMFILE"])
result = ShellUtils.try_command_n_times full_bundle_cmd, 3
end

result = red(result) unless $?.success?
puts result
$?
end

def change_lock_version filepath, gemfile, new_version=Bundler::VERSION
lock_filename = File.join filepath.chomp!, "#{gemfile}.lock"
return unless File.exist? lock_filename

lock_contents = File.read(lock_filename).split("\n")
old_version = lock_contents.pop.strip

lock_contents << " #{new_version}"
File.open(lock_filename, 'w'){|f| f.puts lock_contents}

if verbose?
puts "Changing the Bundler version lock in #{lock_filename}"
puts " Changed: #{old_version} to #{new_version}"
end
end

# Running the bundle should only happen one at a time per Ruby version or
Expand All @@ -117,7 +177,8 @@ def exclusive_bundle
puts "Waiting on '#{bundling_lock_file}' for our chance to bundle" if verbose?
f.flock(File::LOCK_EX)
puts "Let's get ready to BUNDLE!" if verbose?
bundler_out = ShellUtils.try_command_n_times 'bundle install --retry 3 --jobs 4', 3

bundler_out = bundle_install(`pwd`.chomp!)
end
bundler_out
end
Expand All @@ -130,7 +191,9 @@ def ensure_bundle(env_index)
else
ensure_bundle_uncached(env_index)
end
Bundler.require
with_potentially_mismatched_bundler do
Bundler.require
end
end

def envfile_hash
Expand Down Expand Up @@ -232,10 +295,12 @@ def pin_rack_version_if_needed gemfile_text

def print_environment
puts yellow("Environment loaded with:") if verbose?
gems = Bundler.definition.specs.inject([]) do |m, s|
next m if s.name == 'bundler'
m.push "#{s.name} (#{s.version})"
m
gems = with_potentially_mismatched_bundler do
Bundler.definition.specs.inject([]) do |m, s|
next m if s.name == 'bundler'
m.push "#{s.name} (#{s.version})"
m
end
end.sort
puts(gems.join(', '))
end
Expand Down Expand Up @@ -348,12 +413,15 @@ def should_run_environment?(index)
end

def with_unbundled_env
if defined?(Bundler)
# clear $BUNDLE_GEMFILE and $RUBYOPT so that the ruby subprocess can run
# in the context of another bundle.
Bundler.with_unbundled_env { yield }
else
yield
with_potentially_mismatched_bundler do
if defined?(Bundler)
# clear $BUNDLE_GEMFILE and $RUBYOPT so that the ruby subprocess can run
# in the context of another bundle.

Bundler.with_unbundled_env { yield }
else
yield
end
end
end

Expand Down

0 comments on commit 8e58861

Please sign in to comment.