diff --git a/lib/vagrant.rb b/lib/vagrant.rb index e8d46327f..e0458add8 100644 --- a/lib/vagrant.rb +++ b/lib/vagrant.rb @@ -1,314 +1,17 @@ -require 'log4r' -require 'rubygems' +# This file is load before RubyGems are loaded, and allow us to actually +# resolve plugin dependencies and load the proper versions of everything. -# Enable logging if it is requested. We do this before -# anything else so that we can setup the output before -# any logging occurs. -if ENV["VAGRANT_LOG"] && ENV["VAGRANT_LOG"] != "" - # Require Log4r and define the levels we'll be using - require 'log4r/config' - Log4r.define_levels(*Log4r::Log4rConfig::LogLevels) - - level = nil - begin - level = Log4r.const_get(ENV["VAGRANT_LOG"].upcase) - rescue NameError - # This means that the logging constant wasn't found, - # which is fine. We just keep `level` as `nil`. But - # we tell the user. - level = nil - end - - # Some constants, such as "true" resolve to booleans, so the - # above error checking doesn't catch it. This will check to make - # sure that the log level is an integer, as Log4r requires. - level = nil if !level.is_a?(Integer) - - if !level - # We directly write to stderr here because the VagrantError system - # is not setup yet. - $stderr.puts "Invalid VAGRANT_LOG level is set: #{ENV["VAGRANT_LOG"]}" - $stderr.puts "" - $stderr.puts "Please use one of the standard log levels: debug, info, warn, or error" - exit 1 - end - - # Set the logging level on all "vagrant" namespaced - # logs as long as we have a valid level. - if level - logger = Log4r::Logger.new("vagrant") - logger.outputters = Log4r::Outputter.stderr - logger.level = level - logger = nil - end +if defined?(Vagrant) + raise "vagrant is somehow already loaded. bug." end -require 'json' -require 'pathname' -require 'stringio' +ENV["VAGRANT_INSTALLER_EMBEDDED_DIR"] = "/Applications/Vagrant/embedded" -require 'childprocess' -require 'i18n' +# Initialize Bundler before we load _any_ RubyGems. +require_relative "vagrant/bundler" +require_relative "vagrant/plugin_manager" +Vagrant::Bundler.instance.init!(Vagrant::PluginManager.plugins) -# OpenSSL must be loaded here since when it is loaded via `autoload` -# there are issues with ciphers not being properly loaded. -require 'openssl' - -# Always make the version available -require 'vagrant/version' -global_logger = Log4r::Logger.new("vagrant::global") -global_logger.info("Vagrant version: #{Vagrant::VERSION}") -global_logger.info("Ruby version: #{RUBY_VERSION}") -global_logger.info("RubyGems version: #{Gem::VERSION}") -ENV.each do |k, v| - global_logger.info("#{k}=#{v.inspect}") if k =~ /^VAGRANT_/ -end - -# We need these components always so instead of an autoload we -# just require them explicitly here. -require "vagrant/registry" - -module Vagrant - autoload :Action, 'vagrant/action' - autoload :BatchAction, 'vagrant/batch_action' - autoload :Box, 'vagrant/box' - autoload :BoxCollection, 'vagrant/box_collection' - autoload :CLI, 'vagrant/cli' - autoload :Command, 'vagrant/command' - autoload :Config, 'vagrant/config' - autoload :Driver, 'vagrant/driver' - autoload :Environment, 'vagrant/environment' - autoload :Errors, 'vagrant/errors' - autoload :Guest, 'vagrant/guest' - autoload :Hosts, 'vagrant/hosts' - autoload :Machine, 'vagrant/machine' - autoload :MachineState, 'vagrant/machine_state' - autoload :Plugin, 'vagrant/plugin' - autoload :UI, 'vagrant/ui' - autoload :Util, 'vagrant/util' - - # These are the various plugin versions and their components in - # a lazy loaded Hash-like structure. - PLUGIN_COMPONENTS = Registry.new.tap do |c| - c.register(:"1") { Plugin::V1::Plugin } - c.register([:"1", :command]) { Plugin::V1::Command } - c.register([:"1", :communicator]) { Plugin::V1::Communicator } - c.register([:"1", :config]) { Plugin::V1::Config } - c.register([:"1", :guest]) { Plugin::V1::Guest } - c.register([:"1", :host]) { Plugin::V1::Host } - c.register([:"1", :provider]) { Plugin::V1::Provider } - c.register([:"1", :provisioner]) { Plugin::V1::Provisioner } - - c.register(:"2") { Plugin::V2::Plugin } - c.register([:"2", :command]) { Plugin::V2::Command } - c.register([:"2", :communicator]) { Plugin::V2::Communicator } - c.register([:"2", :config]) { Plugin::V2::Config } - c.register([:"2", :guest]) { Plugin::V2::Guest } - c.register([:"2", :host]) { Plugin::V2::Host } - c.register([:"2", :provider]) { Plugin::V2::Provider } - c.register([:"2", :provisioner]) { Plugin::V2::Provisioner } - c.register([:"2", :synced_folder]) { Plugin::V2::SyncedFolder } - end - - # This returns a true/false showing whether we're running from the - # environment setup by the Vagrant installers. - # - # @return [Boolean] - def self.in_installer? - !!ENV["VAGRANT_INSTALLER_ENV"] - end - - # The source root is the path to the root directory of - # the Vagrant gem. - def self.source_root - @source_root ||= Pathname.new(File.expand_path('../../', __FILE__)) - end - - # Configure a Vagrant environment. The version specifies the version - # of the configuration that is expected by the block. The block, based - # on that version, configures the environment. - # - # Note that the block isn't run immediately. Instead, the configuration - # block is stored until later, and is run when an environment is loaded. - # - # @param [String] version Version of the configuration - def self.configure(version, &block) - Config.run(version, &block) - end - - # This checks if a plugin with the given name is installed. This can - # be used from the Vagrantfile to easily branch based on plugin - # availability. - def self.has_plugin?(name) - manager = plugin("2").manager - - manager.required.any? { |gem_name| gem_name == name } || - manager.registered.any? { |plugin| plugin.name == name } - end - - # Returns a superclass to use when creating a plugin for Vagrant. - # Given a specific version, this returns a proper superclass to use - # to register plugins for that version. - # - # Optionally, if you give a specific component, then it will return - # the proper superclass for that component as well. - # - # Plugins and plugin components should subclass the classes returned by - # this method. This method lets Vagrant core control these superclasses - # and change them over time without affecting plugins. For example, if - # the V1 superclass happens to be "Vagrant::V1," future versions of - # Vagrant may move it to "Vagrant::Plugins::V1" and plugins will not be - # affected. - # - # @param [String] version - # @param [String] component - # @return [Class] - def self.plugin(version, component=nil) - # Build up the key and return a result - key = version.to_s.to_sym - key = [key, component.to_s.to_sym] if component - result = PLUGIN_COMPONENTS.get(key) - - # If we found our component then we return that - return result if result - - # If we didn't find a result, then raise an exception, depending - # on if we got a component or not. - raise ArgumentError, "Plugin superclass not found for version/component: " + - "#{version} #{component}" - end - - # This should be used instead of Ruby's built-in `require` in order to - # load a Vagrant plugin. This will load the given plugin by first doing - # a normal `require`, giving a nice error message if things go wrong, - # and second by verifying that a Vagrant plugin was actually defined in - # the process. - # - # @param [String] name Name of the plugin to load. - def self.require_plugin(name) - logger = Log4r::Logger.new("vagrant::root") - - if ENV["VAGRANT_NO_PLUGINS"] - logger.warn("VAGRANT_NO_PLUGINS is set, not loading 3rd party plugin: #{name}") - return - end - - # Redirect stdout/stderr so that we can output it in our own way. - previous_stderr = $stderr - previous_stdout = $stdout - $stderr = StringIO.new - $stdout = StringIO.new - - # Attempt the normal require - begin - require name - plugin("2").manager.plugin_required(name) - rescue Exception => e - # Since this is a rare case, we create a one-time logger here - # in order to output the error - logger.error("Failed to load plugin: #{name}") - logger.error(" -- Error: #{e.inspect}") - logger.error(" -- Backtrace:") - logger.error(e.backtrace.join("\n")) - - # If it is a LoadError we first try to see if it failed loading - # the top-level entrypoint. If so, then we report a different error. - if e.is_a?(LoadError) - # Parse the message in order to get what failed to load, and - # add some extra protection around if the message is different. - parts = e.to_s.split(" -- ", 2) - if parts.length == 2 && parts[1] == name - raise Errors::PluginLoadError, :plugin => name - end - end - - # Get the string data out from the stdout/stderr captures - stderr = $stderr.string - stdout = $stdout.string - if !stderr.empty? || !stdout.empty? - raise Errors::PluginLoadFailedWithOutput, - :plugin => name, - :stderr => stderr, - :stdout => stdout - end - - # And raise an error itself - raise Errors::PluginLoadFailed, - :plugin => name - end - - # Log plugin version - gem = Gem::Specification.find { |spec| spec.name == name } - version = gem ? gem.version : "" - logger.info("Loaded plugin #{name}, version #{version}") - ensure - $stderr = previous_stderr if previous_stderr - $stdout = previous_stdout if previous_stdout - end - - # This allows a Vagrantfile to specify the version of Vagrant that is - # required. You can specify a list of requirements which will all be checked - # against the running Vagrant version. - # - # This should be specified at the _top_ of any Vagrantfile. - # - # Examples are shown below: - # - # Vagrant.require_version(">= 1.3.5") - # Vagrant.require_version(">= 1.3.5", "< 1.4.0") - # Vagrant.require_version("~> 1.3.5") - # - def self.require_version(*requirements) - logger = Log4r::Logger.new("vagrant::root") - logger.info("Version requirements from Vagrantfile: #{requirements.inspect}") - - req = Gem::Requirement.new(*requirements) - if req.satisfied_by?(Gem::Version.new(VERSION)) - logger.info(" - Version requirements satisfied!") - return - end - - raise Errors::VagrantVersionBad, - requirements: requirements.join(", "), - version: VERSION - end -end - -# Default I18n to load the en locale -I18n.load_path << File.expand_path("templates/locales/en.yml", Vagrant.source_root) - -if I18n.config.respond_to?(:enforce_available_locales=) - # Make sure only available locales are used. This will be the default in the - # future but we need this to silence a deprecation warning from 0.6.9 - I18n.config.enforce_available_locales = true -end - -# A lambda that knows how to load plugins from a single directory. -plugin_load_proc = lambda do |directory| - # We only care about directories - next false if !directory.directory? - - # If there is a plugin file in the top-level directory, then load - # that up. - plugin_file = directory.join("plugin.rb") - if plugin_file.file? - global_logger.debug("Loading core plugin: #{plugin_file}") - load(plugin_file) - next true - end -end - -# Go through the `plugins` directory and attempt to load any plugins. The -# plugins are allowed to be in a directory in `plugins` or at most one -# directory deep within the plugins directory. So a plugin can be at -# `plugins/foo` or also at `plugins/foo/bar`, but no deeper. -Vagrant.source_root.join("plugins").children(true).each do |directory| - # Ignore non-directories - next if !directory.directory? - - # Load from this directory, and exit if we successfully loaded a plugin - next if plugin_load_proc.call(directory) - - # Otherwise, attempt to load from sub-directories - directory.children(true).each(&plugin_load_proc) -end +# Initialize Vagrant first, then load the remaining dependencies +require "vagrant/init" +Bundler.require(:default) diff --git a/lib/vagrant/bundler.rb b/lib/vagrant/bundler.rb new file mode 100644 index 000000000..769b30f4e --- /dev/null +++ b/lib/vagrant/bundler.rb @@ -0,0 +1,44 @@ +require "tempfile" + +require_relative "paths" +require_relative "version" + +module Vagrant + # This class manages Vagrant's interaction with Bundler. Vagrant uses + # Bundler as a way to properly resolve all dependencies of Vagrant and + # all Vagrant-installed plugins. + class Bundler + def self.instance + @bundler ||= self.new + end + + # Initializes Bundler and the various gem paths so that we can begin + # loading gems. This must only be called once. + def init!(plugins) + raise "Bundler already initialized" if defined?(::Bundler) + + # Setup the Bundler configuration + @configfile = Tempfile.new("vagrant-bundler-config") + @configfile.close + + # Build up the Gemfile for our Bundler context + @gemfile = Tempfile.new("vagrant-gemfile") + @gemfile.puts(%Q[gem "vagrant", "= #{Vagrant::VERSION}"]) + plugins.each do |plugin| + @gemfile.puts(%Q[gem "#{plugin}"]) + end + @gemfile.close + + # Set the environmental variables for Bundler + ENV["BUNDLE_CONFIG"] = @configfile.path + ENV["BUNDLE_GEMFILE"] = @gemfile.path + ENV["GEM_PATH"] = + "#{Vagrant.user_data_path.join("gems")}#{::File::PATH_SEPARATOR}#{ENV["GEM_PATH"]}" + Gem.clear_paths + + # Load Bundler and setup our paths + require "bundler" + ::Bundler.setup + end + end +end diff --git a/lib/vagrant/environment.rb b/lib/vagrant/environment.rb index 62d059dd1..1365ebc83 100644 --- a/lib/vagrant/environment.rb +++ b/lib/vagrant/environment.rb @@ -128,9 +128,6 @@ module Vagrant @default_private_key_path = @home_path.join("insecure_private_key") copy_insecure_private_key - # Load the plugins - load_plugins - # Call the hooks that does not require configurations to be loaded # by using a "clean" action runner hook(:environment_plugins_loaded, runner: Action::Runner.new(env: self)) @@ -591,7 +588,7 @@ module Vagrant def setup_home_path @home_path = Pathname.new(File.expand_path(@home_path || ENV["VAGRANT_HOME"] || - default_home_path)) + Vagrant.user_data_path)) @logger.info("Home path: #{@home_path}") # Setup the list of child directories that need to be created if they @@ -691,23 +688,6 @@ module Vagrant end end - # This returns the default home directory path for Vagrant, which - # can differ depending on the system. - # - # @return [Pathname] - def default_home_path - path = "~/.vagrant.d" - - # On Windows, we default ot the USERPROFILE directory if it - # is available. This is more compatible with Cygwin and sharing - # the home directory across shells. - if Util::Platform.windows? && ENV["USERPROFILE"] - path = "#{ENV["USERPROFILE"]}/.vagrant.d" - end - - Pathname.new(path) - end - # Finds the Vagrantfile in the given directory. # # @param [Pathname] path Path to search in. @@ -722,55 +702,6 @@ module Vagrant nil end - # Loads the Vagrant plugins by properly setting up RubyGems so that - # our private gem repository is on the path. - def load_plugins - # Add our private gem path to the gem path and reset the paths - # that Rubygems knows about. - ENV["GEM_PATH"] = "#{@gems_path}#{::File::PATH_SEPARATOR}#{ENV["GEM_PATH"]}" - ::Gem.clear_paths - - # If we're in a Bundler environment, don't load plugins. This only - # happens in plugin development environments. - if defined?(Bundler) - require 'bundler/shared_helpers' - if Bundler::SharedHelpers.in_bundle? - @logger.warn("In a bundler environment, not loading environment plugins!") - return - end - end - - # This keeps track of the old plugins that need to be reinstalled - # because they were installed with an old version of Ruby. - reinstall = [] - - # Load the plugins - plugins_json_file = @home_path.join("plugins.json") - @logger.debug("Loading plugins from: #{plugins_json_file}") - state = VagrantPlugins::CommandPlugin::StateFile.new(plugins_json_file) - state.installed_plugins.each do |name, extra| - # If the Ruby version changed, then they need to reinstall the plugin - if extra["ruby_version"] != RUBY_VERSION - reinstall << name - next - end - - @logger.info("Loading plugin from JSON: #{name}") - begin - Vagrant.require_plugin(name) - rescue Errors::PluginLoadError => e - @ui.error(e.message + "\n") - rescue Errors::PluginLoadFailed => e - @ui.error(e.message + "\n") - end - end - - if !reinstall.empty? - @ui.warn(I18n.t("vagrant.plugin_needs_reinstall", - names: reinstall.join(", "))) - end - end - # This upgrades a Vagrant 1.0.x "dotfile" to the new V2 format. # # This is a destructive process. Once the upgrade is complete, the diff --git a/lib/vagrant/init.rb b/lib/vagrant/init.rb new file mode 100644 index 000000000..c4a17be6b --- /dev/null +++ b/lib/vagrant/init.rb @@ -0,0 +1,308 @@ +require 'rubygems' +require 'log4r' + +# Enable logging if it is requested. We do this before +# anything else so that we can setup the output before +# any logging occurs. +if ENV["VAGRANT_LOG"] && ENV["VAGRANT_LOG"] != "" + # Require Log4r and define the levels we'll be using + require 'log4r/config' + Log4r.define_levels(*Log4r::Log4rConfig::LogLevels) + + level = nil + begin + level = Log4r.const_get(ENV["VAGRANT_LOG"].upcase) + rescue NameError + # This means that the logging constant wasn't found, + # which is fine. We just keep `level` as `nil`. But + # we tell the user. + level = nil + end + + # Some constants, such as "true" resolve to booleans, so the + # above error checking doesn't catch it. This will check to make + # sure that the log level is an integer, as Log4r requires. + level = nil if !level.is_a?(Integer) + + if !level + # We directly write to stderr here because the VagrantError system + # is not setup yet. + $stderr.puts "Invalid VAGRANT_LOG level is set: #{ENV["VAGRANT_LOG"]}" + $stderr.puts "" + $stderr.puts "Please use one of the standard log levels: debug, info, warn, or error" + exit 1 + end + + # Set the logging level on all "vagrant" namespaced + # logs as long as we have a valid level. + if level + logger = Log4r::Logger.new("vagrant") + logger.outputters = Log4r::Outputter.stderr + logger.level = level + logger = nil + end +end + +require 'json' +require 'pathname' +require 'stringio' + +require 'childprocess' +require 'i18n' + +# OpenSSL must be loaded here since when it is loaded via `autoload` +# there are issues with ciphers not being properly loaded. +require 'openssl' + +# Always make the version available +require 'vagrant/version' +global_logger = Log4r::Logger.new("vagrant::global") +global_logger.info("Vagrant version: #{Vagrant::VERSION}") +global_logger.info("Ruby version: #{RUBY_VERSION}") +global_logger.info("RubyGems version: #{Gem::VERSION}") +ENV.each do |k, v| + global_logger.info("#{k}=#{v.inspect}") if k =~ /^VAGRANT_/ +end + +# We need these components always so instead of an autoload we +# just require them explicitly here. +require "vagrant/registry" + +module Vagrant + autoload :Action, 'vagrant/action' + autoload :BatchAction, 'vagrant/batch_action' + autoload :Box, 'vagrant/box' + autoload :BoxCollection, 'vagrant/box_collection' + autoload :CLI, 'vagrant/cli' + autoload :Command, 'vagrant/command' + autoload :Config, 'vagrant/config' + autoload :Driver, 'vagrant/driver' + autoload :Environment, 'vagrant/environment' + autoload :Errors, 'vagrant/errors' + autoload :Guest, 'vagrant/guest' + autoload :Hosts, 'vagrant/hosts' + autoload :Machine, 'vagrant/machine' + autoload :MachineState, 'vagrant/machine_state' + autoload :Plugin, 'vagrant/plugin' + autoload :UI, 'vagrant/ui' + autoload :Util, 'vagrant/util' + + # These are the various plugin versions and their components in + # a lazy loaded Hash-like structure. + PLUGIN_COMPONENTS = Registry.new.tap do |c| + c.register(:"1") { Plugin::V1::Plugin } + c.register([:"1", :command]) { Plugin::V1::Command } + c.register([:"1", :communicator]) { Plugin::V1::Communicator } + c.register([:"1", :config]) { Plugin::V1::Config } + c.register([:"1", :guest]) { Plugin::V1::Guest } + c.register([:"1", :host]) { Plugin::V1::Host } + c.register([:"1", :provider]) { Plugin::V1::Provider } + c.register([:"1", :provisioner]) { Plugin::V1::Provisioner } + + c.register(:"2") { Plugin::V2::Plugin } + c.register([:"2", :command]) { Plugin::V2::Command } + c.register([:"2", :communicator]) { Plugin::V2::Communicator } + c.register([:"2", :config]) { Plugin::V2::Config } + c.register([:"2", :guest]) { Plugin::V2::Guest } + c.register([:"2", :host]) { Plugin::V2::Host } + c.register([:"2", :provider]) { Plugin::V2::Provider } + c.register([:"2", :provisioner]) { Plugin::V2::Provisioner } + c.register([:"2", :synced_folder]) { Plugin::V2::SyncedFolder } + end + + # This returns a true/false showing whether we're running from the + # environment setup by the Vagrant installers. + # + # @return [Boolean] + def self.in_installer? + !!ENV["VAGRANT_INSTALLER_ENV"] + end + + # Configure a Vagrant environment. The version specifies the version + # of the configuration that is expected by the block. The block, based + # on that version, configures the environment. + # + # Note that the block isn't run immediately. Instead, the configuration + # block is stored until later, and is run when an environment is loaded. + # + # @param [String] version Version of the configuration + def self.configure(version, &block) + Config.run(version, &block) + end + + # This checks if a plugin with the given name is installed. This can + # be used from the Vagrantfile to easily branch based on plugin + # availability. + def self.has_plugin?(name) + manager = plugin("2").manager + + manager.required.any? { |gem_name| gem_name == name } || + manager.registered.any? { |plugin| plugin.name == name } + end + + # Returns a superclass to use when creating a plugin for Vagrant. + # Given a specific version, this returns a proper superclass to use + # to register plugins for that version. + # + # Optionally, if you give a specific component, then it will return + # the proper superclass for that component as well. + # + # Plugins and plugin components should subclass the classes returned by + # this method. This method lets Vagrant core control these superclasses + # and change them over time without affecting plugins. For example, if + # the V1 superclass happens to be "Vagrant::V1," future versions of + # Vagrant may move it to "Vagrant::Plugins::V1" and plugins will not be + # affected. + # + # @param [String] version + # @param [String] component + # @return [Class] + def self.plugin(version, component=nil) + # Build up the key and return a result + key = version.to_s.to_sym + key = [key, component.to_s.to_sym] if component + result = PLUGIN_COMPONENTS.get(key) + + # If we found our component then we return that + return result if result + + # If we didn't find a result, then raise an exception, depending + # on if we got a component or not. + raise ArgumentError, "Plugin superclass not found for version/component: " + + "#{version} #{component}" + end + + # This should be used instead of Ruby's built-in `require` in order to + # load a Vagrant plugin. This will load the given plugin by first doing + # a normal `require`, giving a nice error message if things go wrong, + # and second by verifying that a Vagrant plugin was actually defined in + # the process. + # + # @param [String] name Name of the plugin to load. + def self.require_plugin(name) + logger = Log4r::Logger.new("vagrant::root") + + if ENV["VAGRANT_NO_PLUGINS"] + logger.warn("VAGRANT_NO_PLUGINS is set, not loading 3rd party plugin: #{name}") + return + end + + # Redirect stdout/stderr so that we can output it in our own way. + previous_stderr = $stderr + previous_stdout = $stdout + $stderr = StringIO.new + $stdout = StringIO.new + + # Attempt the normal require + begin + require name + plugin("2").manager.plugin_required(name) + rescue Exception => e + # Since this is a rare case, we create a one-time logger here + # in order to output the error + logger.error("Failed to load plugin: #{name}") + logger.error(" -- Error: #{e.inspect}") + logger.error(" -- Backtrace:") + logger.error(e.backtrace.join("\n")) + + # If it is a LoadError we first try to see if it failed loading + # the top-level entrypoint. If so, then we report a different error. + if e.is_a?(LoadError) + # Parse the message in order to get what failed to load, and + # add some extra protection around if the message is different. + parts = e.to_s.split(" -- ", 2) + if parts.length == 2 && parts[1] == name + raise Errors::PluginLoadError, :plugin => name + end + end + + # Get the string data out from the stdout/stderr captures + stderr = $stderr.string + stdout = $stdout.string + if !stderr.empty? || !stdout.empty? + raise Errors::PluginLoadFailedWithOutput, + :plugin => name, + :stderr => stderr, + :stdout => stdout + end + + # And raise an error itself + raise Errors::PluginLoadFailed, + :plugin => name + end + + # Log plugin version + gem = Gem::Specification.find { |spec| spec.name == name } + version = gem ? gem.version : "" + logger.info("Loaded plugin #{name}, version #{version}") + ensure + $stderr = previous_stderr if previous_stderr + $stdout = previous_stdout if previous_stdout + end + + # This allows a Vagrantfile to specify the version of Vagrant that is + # required. You can specify a list of requirements which will all be checked + # against the running Vagrant version. + # + # This should be specified at the _top_ of any Vagrantfile. + # + # Examples are shown below: + # + # Vagrant.require_version(">= 1.3.5") + # Vagrant.require_version(">= 1.3.5", "< 1.4.0") + # Vagrant.require_version("~> 1.3.5") + # + def self.require_version(*requirements) + logger = Log4r::Logger.new("vagrant::root") + logger.info("Version requirements from Vagrantfile: #{requirements.inspect}") + + req = Gem::Requirement.new(*requirements) + if req.satisfied_by?(Gem::Version.new(VERSION)) + logger.info(" - Version requirements satisfied!") + return + end + + raise Errors::VagrantVersionBad, + requirements: requirements.join(", "), + version: VERSION + end +end + +# Default I18n to load the en locale +I18n.load_path << File.expand_path("templates/locales/en.yml", Vagrant.source_root) + +if I18n.config.respond_to?(:enforce_available_locales=) + # Make sure only available locales are used. This will be the default in the + # future but we need this to silence a deprecation warning from 0.6.9 + I18n.config.enforce_available_locales = true +end + +# A lambda that knows how to load plugins from a single directory. +plugin_load_proc = lambda do |directory| + # We only care about directories + next false if !directory.directory? + + # If there is a plugin file in the top-level directory, then load + # that up. + plugin_file = directory.join("plugin.rb") + if plugin_file.file? + global_logger.debug("Loading core plugin: #{plugin_file}") + load(plugin_file) + next true + end +end + +# Go through the `plugins` directory and attempt to load any plugins. The +# plugins are allowed to be in a directory in `plugins` or at most one +# directory deep within the plugins directory. So a plugin can be at +# `plugins/foo` or also at `plugins/foo/bar`, but no deeper. +Vagrant.source_root.join("plugins").children(true).each do |directory| + # Ignore non-directories + next if !directory.directory? + + # Load from this directory, and exit if we successfully loaded a plugin + next if plugin_load_proc.call(directory) + + # Otherwise, attempt to load from sub-directories + directory.children(true).each(&plugin_load_proc) +end diff --git a/lib/vagrant/paths.rb b/lib/vagrant/paths.rb new file mode 100644 index 000000000..6871e16f0 --- /dev/null +++ b/lib/vagrant/paths.rb @@ -0,0 +1,26 @@ +require "pathname" + +module Vagrant + # The source root is the path to the root directory of + # the Vagrant gem. + def self.source_root + @source_root ||= Pathname.new(File.expand_path('../../../', __FILE__)) + end + + # This returns the path to the ~/.vagrant.d folder where Vagrant's + # per-user state is stored. + # + # @return [Pathname] + def self.user_data_path + path = "~/.vagrant.d" + + # On Windows, we default ot the USERPROFILE directory if it + # is available. This is more compatible with Cygwin and sharing + # the home directory across shells. + if ENV["USERPROFILE"] + path = "#{ENV["USERPROFILE"]}/.vagrant.d" + end + + return Pathname.new(path).expand_path + end +end diff --git a/lib/vagrant/plugin_manager.rb b/lib/vagrant/plugin_manager.rb new file mode 100644 index 000000000..4379af569 --- /dev/null +++ b/lib/vagrant/plugin_manager.rb @@ -0,0 +1,16 @@ +require "json" + +require_relative "paths" + +module Vagrant + class PluginManager + def self.global_plugins_file + Vagrant.user_data_path.join("plugins.json") + end + + def self.plugins + plugins = JSON.parse(global_plugins_file.read) + plugins["installed"].keys + end + end +end diff --git a/vagrant.gemspec b/vagrant.gemspec index 6c2a1ae28..6238af891 100644 --- a/vagrant.gemspec +++ b/vagrant.gemspec @@ -14,6 +14,7 @@ Gem::Specification.new do |s| s.required_rubygems_version = ">= 1.3.6" s.rubyforge_project = "vagrant" + s.add_dependency "bundler", "~> 1.5.1" s.add_dependency "childprocess", "~> 0.3.7" s.add_dependency "erubis", "~> 2.7.0" s.add_dependency "i18n", "~> 0.6.0" @@ -25,8 +26,6 @@ Gem::Specification.new do |s| s.add_development_dependency "contest", ">= 0.1.2" s.add_development_dependency "minitest", "~> 2.5.1" s.add_development_dependency "mocha" - # This has problems on Windows, we need to find a better way: - # s.add_development_dependency "sys-proctable", "~> 0.9.0" s.add_development_dependency "rspec", "~> 2.14.0" # The following block of code determines the files that should be included