diff --git a/bin/vagrant b/bin/vagrant index fce68c8..781fc63 100755 --- a/bin/vagrant +++ b/bin/vagrant @@ -69,6 +69,11 @@ end require "bundler" begin $vagrant_bundler_runtime = Bundler.setup(:default, :plugins) +# Invalidate the cached Gemfile.lock if necessary and try again +rescue Bundler::GemNotFound + FileUtils.rm File.expand_path("~/.vagrant.d/Gemfile") if File.exists? File.expand_path("~/.vagrant.d/Gemfile") + FileUtils.rm File.expand_path("~/.vagrant.d/Gemfile.lock") if File.exists? File.expand_path("~/.vagrant.d/Gemfile.lock") + $vagrant_bundler_runtime = Bundler.setup(:default, :plugins) rescue Bundler::GemNotFound $stderr.puts "Bundler, the underlying system used to manage Vagrant plugins," $stderr.puts "is reporting that a plugin or its dependency can't be found." diff --git a/lib/vagrant/bundler.rb b/lib/vagrant/bundler.rb index ea8c056..0543963 100644 --- a/lib/vagrant/bundler.rb +++ b/lib/vagrant/bundler.rb @@ -63,6 +63,13 @@ module Vagrant @configfile = File.open(Tempfile.new("vagrant").path + "1", "w+") @configfile.close + # Ensure the path to user's Gemfile exists + gemfile = Vagrant.user_data_path.join("Gemfile") + unless File.exists? gemfile + FileUtils.mkdir_p(File.dirname(gemfile)) + File.open(gemfile, 'w') {} + end + # Build up the Gemfile for our Bundler context. We make sure to # lock Vagrant to our current Vagrant version. In addition to that, # we add all our plugin dependencies. @@ -141,7 +148,7 @@ module Vagrant # Clean removes any unused gems. def clean(plugins) - gemfile = build_gemfile(plugins) + gemfile = build_gemfile(plugins, false, true) lockfile = "#{gemfile.path}.lock" definition = ::Bundler::Definition.build(gemfile, lockfile, nil) root = File.dirname(gemfile.path) @@ -172,11 +179,24 @@ module Vagrant # Builds a valid Gemfile for use with Bundler given the list of # plugins. # + # @param [Hash|Bool] update Hash of gems to update or true for all + # @param [Bool] invalidate Invalidate Gemfile.lock # @return [Tempfile] - def build_gemfile(plugins) + def build_gemfile(plugins, update = false, invalidate = false) sources = plugins.values.map { |p| p["sources"] }.flatten.compact.uniq - f = File.open(Tempfile.new("vagrant").path + "2", "w+") + # Determine what gems to update + if update.is_a? Hash + update_gems = update[:gems] + elsif update === true + update_gems = plugins.map{ |p| p[0] } + else + update_gems = [] + end + + gemfile = Vagrant.user_data_path.join("Gemfile") + f = File.open(gemfile, "w+") + f.tap do |gemfile| if !sources.include?("http://rubygems.org") gemfile.puts(%Q[source "https://rubygems.org"]) @@ -190,6 +210,19 @@ module Vagrant gemfile.puts(%Q[gemspec :path => "#{File.expand_path '../../..', __FILE__}"]) + locked_gems = [] + + # Use Gemfile.lock to lock the gem versions + if ENV["VAGRANT_INTERNAL_BUNDLERIZED"] && File.exist?("#{gemfile.path}.lock") && !invalidate + lockfile = ::Bundler::LockfileParser.new(::Bundler.read_file("#{gemfile.path}.lock")) + lockfile.specs.each do |s| + if s.name != 'vagrant' && !(update_gems.include? s.name) + gemfile.puts(%Q[gem "#{s.name}", "#{s.version.to_s}"]) + end + end + locked_gems = lockfile.specs.map(&:name) - update_gems + end + gemfile.puts("group :plugins do") plugins.each do |name, plugin| version = plugin["gem_version"] @@ -199,13 +232,19 @@ module Vagrant if plugin["require"] && plugin["require"] != "" opts[:require] = plugin["require"] end - - gemfile.puts(%Q[gem "#{name}", #{version.inspect}, #{opts.inspect}]) + gemfile.puts(%Q[gem "#{name}", #{version.inspect}, #{opts.inspect}]) unless locked_gems.include? name end gemfile.puts("end") - gemfile.close end + + # Create Gemfile.lock if missing and re-generate Gemfile + if !File.exist?("#{f.path}.lock") && File.exist?(f.path) + lockfile = "#{f.path}.lock" + ENV['BUNDLE_GEMFILE'] = f.path + definition = ::Bundler::Definition.build(f.path, lockfile, false) + end + f end # This installs a set of plugins and optionally updates those gems. @@ -215,7 +254,7 @@ module Vagrant # can be a hash of options. See Bundler.definition. # @return [Array] def internal_install(plugins, update, **extra) - gemfile = build_gemfile(plugins) + gemfile = build_gemfile(plugins, update) lockfile = "#{gemfile.path}.lock" definition = ::Bundler::Definition.build(gemfile, lockfile, update) root = File.dirname(gemfile.path)