Merge branch 'f-vagrant-gem'

This commit is contained in:
Mitchell Hashimoto 2012-02-07 22:42:34 -08:00
commit fbcb2c6ddc
8 changed files with 107 additions and 25 deletions

View File

@ -1,6 +1,13 @@
## 1.0.0 (unreleased) ## 1.0.0 (unreleased)
- `vagrant gem` should now be used to install Vagrant plugins that are
gems. This installs the gems to a private gem folder that Vagrant adds
to its own load path. This isolates Vagrant-related gems from system
gems.
- Plugin loading no longer happens right when Vagrant is loaded, but when
a Vagrant environment is loaded. I don't anticipate this causing any
problems but it is a backwards incompatible change should a plugin
depend on this (but I don't see any reason why they would).
## 0.9.6 (February 7, 2012) ## 0.9.6 (February 7, 2012)

View File

@ -50,6 +50,9 @@ require 'i18n'
# there are issues with ciphers not being properly loaded. # there are issues with ciphers not being properly loaded.
require 'openssl' require 'openssl'
# Always make the version available
require 'vagrant/version'
module Vagrant module Vagrant
autoload :Action, 'vagrant/action' autoload :Action, 'vagrant/action'
autoload :Box, 'vagrant/box' autoload :Box, 'vagrant/box'
@ -136,6 +139,7 @@ I18n.load_path << File.expand_path("templates/locales/en.yml", Vagrant.source_ro
# Register the built-in commands # Register the built-in commands
Vagrant.commands.register(:box) { Vagrant::Command::Box } Vagrant.commands.register(:box) { Vagrant::Command::Box }
Vagrant.commands.register(:destroy) { Vagrant::Command::Destroy } Vagrant.commands.register(:destroy) { Vagrant::Command::Destroy }
Vagrant.commands.register(:gem) { Vagrant::Command::Gem }
Vagrant.commands.register(:halt) { Vagrant::Command::Halt } Vagrant.commands.register(:halt) { Vagrant::Command::Halt }
Vagrant.commands.register(:init) { Vagrant::Command::Init } Vagrant.commands.register(:init) { Vagrant::Command::Init }
Vagrant.commands.register(:package) { Vagrant::Command::Package } Vagrant.commands.register(:package) { Vagrant::Command::Package }
@ -186,7 +190,3 @@ Vagrant.provisioners.register(:shell) { Vagrant::Provisioners::Shell }
Vagrant.config_keys.register(:freebsd) { Vagrant::Guest::FreeBSD::FreeBSDConfig } Vagrant.config_keys.register(:freebsd) { Vagrant::Guest::FreeBSD::FreeBSDConfig }
Vagrant.config_keys.register(:linux) { Vagrant::Guest::Linux::LinuxConfig } Vagrant.config_keys.register(:linux) { Vagrant::Guest::Linux::LinuxConfig }
Vagrant.config_keys.register(:solaris) { Vagrant::Guest::Solaris::SolarisConfig } Vagrant.config_keys.register(:solaris) { Vagrant::Guest::Solaris::SolarisConfig }
# Load the things which must be loaded before anything else.
require 'vagrant/version'
Vagrant::Plugin.load!

View File

@ -8,6 +8,7 @@ module Vagrant
autoload :BoxRepackage, 'vagrant/command/box_repackage' autoload :BoxRepackage, 'vagrant/command/box_repackage'
autoload :BoxList, 'vagrant/command/box_list' autoload :BoxList, 'vagrant/command/box_list'
autoload :Destroy, 'vagrant/command/destroy' autoload :Destroy, 'vagrant/command/destroy'
autoload :Gem, 'vagrant/command/gem'
autoload :Halt, 'vagrant/command/halt' autoload :Halt, 'vagrant/command/halt'
autoload :Init, 'vagrant/command/init' autoload :Init, 'vagrant/command/init'
autoload :Package, 'vagrant/command/package' autoload :Package, 'vagrant/command/package'

View File

@ -0,0 +1,35 @@
require "rubygems"
require "rubygems/gem_runner"
module Vagrant
module Command
class Gem < Base
def execute
# Bundler sets up its own custom gem load paths such that our
# own gems are never loaded. Therefore, give an error if a user
# tries to install gems while within a Bundler-managed environment.
if defined?(Bundler)
require 'bundler/shared_helpers'
if Bundler::SharedHelpers.in_bundle?
raise Errors::GemCommandInBundler
end
end
# If the user needs some help, we add our own little message at the
# top so that they're aware of what `vagrant gem` is doing, really.
if @argv.empty? || @argv.include?("-h") || @argv.include?("--help")
@env.ui.info(I18n.t("vagrant.commands.gem.help_preamble"),
:prefix => false)
puts
end
# We just proxy the arguments onto a real RubyGems command
# but change `GEM_HOME` so that the gems are installed into
# our own private gem folder.
ENV["GEM_HOME"] = @env.gems_path.to_s
::Gem.clear_paths
::Gem::GemRunner.new.run(@argv.dup)
end
end
end
end

View File

@ -11,7 +11,7 @@ module Vagrant
# defined as basically a folder with a "Vagrantfile." This class allows # defined as basically a folder with a "Vagrantfile." This class allows
# access to the VMs, CLI, etc. all in the scope of this environment. # access to the VMs, CLI, etc. all in the scope of this environment.
class Environment class Environment
HOME_SUBDIRS = ["tmp", "boxes"] HOME_SUBDIRS = ["tmp", "boxes", "gems"]
DEFAULT_VM = :default DEFAULT_VM = :default
DEFAULT_HOME = "~/.vagrant.d" DEFAULT_HOME = "~/.vagrant.d"
@ -34,6 +34,9 @@ module Vagrant
# The directory where boxes are stored. # The directory where boxes are stored.
attr_reader :boxes_path attr_reader :boxes_path
# The path where the plugins are stored (gems)
attr_reader :gems_path
# The path to the default private key # The path to the default private key
attr_reader :default_private_key_path attr_reader :default_private_key_path
@ -80,10 +83,14 @@ module Vagrant
setup_home_path setup_home_path
@tmp_path = @home_path.join("tmp") @tmp_path = @home_path.join("tmp")
@boxes_path = @home_path.join("boxes") @boxes_path = @home_path.join("boxes")
@gems_path = @home_path.join("gems")
# Setup the default private key # Setup the default private key
@default_private_key_path = @home_path.join("insecure_private_key") @default_private_key_path = @home_path.join("insecure_private_key")
copy_insecure_private_key copy_insecure_private_key
# Load the plugins
load_plugins
end end
#--------------------------------------------------------------- #---------------------------------------------------------------
@ -490,5 +497,17 @@ module Vagrant
nil nil
end 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}:#{ENV["GEM_PATH"]}"
::Gem.clear_paths
# Load the plugins
Plugin.load!
end
end end
end end

View File

@ -173,6 +173,11 @@ module Vagrant
error_key(:environment_locked) error_key(:environment_locked)
end end
class GemCommandInBundler < VagrantError
status_code(71)
error_key(:gem_command_in_bundler)
end
class HomeDirectoryMigrationFailed < VagrantError class HomeDirectoryMigrationFailed < VagrantError
status_code(53) status_code(53)
error_key(:home_dir_migration_failed) error_key(:home_dir_migration_failed)

View File

@ -1,5 +1,7 @@
require "rubygems" require "rubygems"
require "log4r"
module Vagrant module Vagrant
# Represents a single plugin and also manages loading plugins from # Represents a single plugin and also manages loading plugins from
# RubyGems. If a plugin has a `vagrant_init.rb` file somewhere on its # RubyGems. If a plugin has a `vagrant_init.rb` file somewhere on its
@ -7,20 +9,17 @@ module Vagrant
# (for debugging), the list of loaded plugins is stored in the {plugins} # (for debugging), the list of loaded plugins is stored in the {plugins}
# array. # array.
class Plugin class Plugin
# The array of loaded plugins. # The array of gem specifications that were loaded as plugins.
@@plugins = [] @@plugins = []
# The gemspec of this plugin. This is an actual gemspec object.
attr_reader :gemspec
# The path to the `vagrant_init.rb` file which was loaded for this plugin.
attr_reader :file
# Loads all the plugins for Vagrant. Plugins are currently # Loads all the plugins for Vagrant. Plugins are currently
# gems which have a "vagrant_init.rb" somewhere on their # gems which have a "vagrant_init.rb" somewhere on their
# load path. This file is loaded to kick off the load sequence # load path. This file is loaded to kick off the load sequence
# for that plugin. # for that plugin.
def self.load! def self.load!
logger = Log4r::Logger.new("vagrant::plugin")
logger.info("Searching and loading any available plugins...")
# Our version is used for checking dependencies # Our version is used for checking dependencies
our_version = Gem::Version.create(Vagrant::VERSION) our_version = Gem::Version.create(Vagrant::VERSION)
@ -41,10 +40,18 @@ module Vagrant
specs = Gem::VERSION >= "1.6.0" ? source.latest_specs(true) : source.latest_specs specs = Gem::VERSION >= "1.6.0" ? source.latest_specs(true) : source.latest_specs
specs.each do |spec| specs.each do |spec|
if @@plugins.include?(spec)
logger.debug("Plugin already loaded, not loading again: #{spec.name}")
next
end
# If this gem depends on Vagrant, verify this is a valid release of # If this gem depends on Vagrant, verify this is a valid release of
# Vagrant for this gem to load into. # Vagrant for this gem to load into.
vagrant_dep = spec.dependencies.find { |d| d.name == "vagrant" } vagrant_dep = spec.dependencies.find { |d| d.name == "vagrant" }
next if vagrant_dep && !vagrant_dep.requirement.satisfied_by?(our_version) if vagrant_dep && !vagrant_dep.requirement.satisfied_by?(our_version)
logger.debug("Plugin Vagrant dependency mismatch: #{spec.name} (#{spec.version})")
next
end
# Find a vagrant_init.rb to verify if this is a plugin # Find a vagrant_init.rb to verify if this is a plugin
file = nil file = nil
@ -55,8 +62,13 @@ module Vagrant
end end
next if !file next if !file
@@plugins << new(spec, file)
logger.info("Loading plugin: #{spec.name} (#{spec.version})")
@@plugins << spec
load file
end end
logger.info("Loaded #{@@plugins.length} plugins.")
end end
end end
@ -65,15 +77,5 @@ module Vagrant
# #
# @return [Array] # @return [Array]
def self.plugins; @@plugins; end def self.plugins; @@plugins; end
# Initializes a new plugin, given a Gemspec and the path to the
# gem's `vagrant_init.rb` file. This should never be called manually.
# Instead {load!} creates all the instances.
def initialize(spec, file)
@gemspec = spec
@file = file
load file
end
end end
end end

View File

@ -47,6 +47,12 @@ en:
may run at any given time to avoid problems with VirtualBox inconsistencies may run at any given time to avoid problems with VirtualBox inconsistencies
occurring. Please wait for the other instance of Vagrant to end and then occurring. Please wait for the other instance of Vagrant to end and then
try again. try again.
gem_command_in_bundler: |-
You cannot run the `vagrant gem` command while in a bundler environment.
Bundler messes around quite a bit with the RubyGem load paths and gems
installed via `vagrant gem` are excluded by Bundler.
Instead, please include your Vagrant plugins in your Gemfile itself.
guest: guest:
invalid_class: |- invalid_class: |-
The specified guest class does not inherit from `Vagrant::Guest::Base`. The specified guest class does not inherit from `Vagrant::Guest::Base`.
@ -225,6 +231,13 @@ en:
vm_not_running: "VM is not currently running. Please bring it up to run this command." vm_not_running: "VM is not currently running. Please bring it up to run this command."
box: box:
no_installed_boxes: "There are no installed boxes! Use `vagrant box add` to add some." no_installed_boxes: "There are no installed boxes! Use `vagrant box add` to add some."
gem:
help_preamble: |-
`vagrant gem` is used to install Vagrant plugins via the RubyGems
system. In fact, `vagrant gem` is just a frontend to the actual `gem`
interface, with the difference being that Vagrant sets up a custom
directory where gems are installed so that they are isolated from your
system gems.
init: init:
success: |- success: |-
A `Vagrantfile` has been placed in this directory. You are now A `Vagrantfile` has been placed in this directory. You are now