Skip to content

Commit

Permalink
Merge branch 'f-vagrant-gem'
Browse files Browse the repository at this point in the history
  • Loading branch information
mitchellh committed Feb 8, 2012
2 parents 6224f79 + a3d9615 commit fbcb2c6
Show file tree
Hide file tree
Showing 8 changed files with 107 additions and 25 deletions.
9 changes: 8 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
## 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)

Expand Down
8 changes: 4 additions & 4 deletions lib/vagrant.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@
# there are issues with ciphers not being properly loaded.
require 'openssl'

# Always make the version available
require 'vagrant/version'

module Vagrant
autoload :Action, 'vagrant/action'
autoload :Box, 'vagrant/box'
Expand Down Expand Up @@ -136,6 +139,7 @@ def self.provisioners
# Register the built-in commands
Vagrant.commands.register(:box) { Vagrant::Command::Box }
Vagrant.commands.register(:destroy) { Vagrant::Command::Destroy }
Vagrant.commands.register(:gem) { Vagrant::Command::Gem }
Vagrant.commands.register(:halt) { Vagrant::Command::Halt }
Vagrant.commands.register(:init) { Vagrant::Command::Init }
Vagrant.commands.register(:package) { Vagrant::Command::Package }
Expand Down Expand Up @@ -186,7 +190,3 @@ def self.provisioners
Vagrant.config_keys.register(:freebsd) { Vagrant::Guest::FreeBSD::FreeBSDConfig }
Vagrant.config_keys.register(:linux) { Vagrant::Guest::Linux::LinuxConfig }
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!
1 change: 1 addition & 0 deletions lib/vagrant/command.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ module Command
autoload :BoxRepackage, 'vagrant/command/box_repackage'
autoload :BoxList, 'vagrant/command/box_list'
autoload :Destroy, 'vagrant/command/destroy'
autoload :Gem, 'vagrant/command/gem'
autoload :Halt, 'vagrant/command/halt'
autoload :Init, 'vagrant/command/init'
autoload :Package, 'vagrant/command/package'
Expand Down
35 changes: 35 additions & 0 deletions lib/vagrant/command/gem.rb
Original file line number Diff line number Diff line change
@@ -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
21 changes: 20 additions & 1 deletion lib/vagrant/environment.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ module Vagrant
# defined as basically a folder with a "Vagrantfile." This class allows
# access to the VMs, CLI, etc. all in the scope of this environment.
class Environment
HOME_SUBDIRS = ["tmp", "boxes"]
HOME_SUBDIRS = ["tmp", "boxes", "gems"]
DEFAULT_VM = :default
DEFAULT_HOME = "~/.vagrant.d"

Expand All @@ -34,6 +34,9 @@ class Environment
# The directory where boxes are stored.
attr_reader :boxes_path

# The path where the plugins are stored (gems)
attr_reader :gems_path

# The path to the default private key
attr_reader :default_private_key_path

Expand Down Expand Up @@ -80,10 +83,14 @@ def initialize(opts=nil)
setup_home_path
@tmp_path = @home_path.join("tmp")
@boxes_path = @home_path.join("boxes")
@gems_path = @home_path.join("gems")

# Setup the default private key
@default_private_key_path = @home_path.join("insecure_private_key")
copy_insecure_private_key

# Load the plugins
load_plugins
end

#---------------------------------------------------------------
Expand Down Expand Up @@ -490,5 +497,17 @@ def find_vagrantfile(search_path)

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}:#{ENV["GEM_PATH"]}"
::Gem.clear_paths

# Load the plugins
Plugin.load!
end
end
end
5 changes: 5 additions & 0 deletions lib/vagrant/errors.rb
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,11 @@ class EnvironmentLockedError < VagrantError
error_key(:environment_locked)
end

class GemCommandInBundler < VagrantError
status_code(71)
error_key(:gem_command_in_bundler)
end

class HomeDirectoryMigrationFailed < VagrantError
status_code(53)
error_key(:home_dir_migration_failed)
Expand Down
40 changes: 21 additions & 19 deletions lib/vagrant/plugin.rb
Original file line number Diff line number Diff line change
@@ -1,26 +1,25 @@
require "rubygems"

require "log4r"

module Vagrant
# Represents a single plugin and also manages loading plugins from
# RubyGems. If a plugin has a `vagrant_init.rb` file somewhere on its
# load path, then this class will find it and load it. For logging purposes
# (for debugging), the list of loaded plugins is stored in the {plugins}
# array.
class Plugin
# The array of loaded plugins.
# The array of gem specifications that were loaded as 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
# gems which have a "vagrant_init.rb" somewhere on their
# load path. This file is loaded to kick off the load sequence
# for that plugin.
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 = Gem::Version.create(Vagrant::VERSION)

Expand All @@ -41,10 +40,18 @@ def self.load!
specs = Gem::VERSION >= "1.6.0" ? source.latest_specs(true) : source.latest_specs

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
# Vagrant for this gem to load into.
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
file = nil
Expand All @@ -55,8 +62,13 @@ def self.load!
end

next if !file
@@plugins << new(spec, file)

logger.info("Loading plugin: #{spec.name} (#{spec.version})")
@@plugins << spec
load file
end

logger.info("Loaded #{@@plugins.length} plugins.")
end
end

Expand All @@ -65,15 +77,5 @@ def self.load!
#
# @return [Array]
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
13 changes: 13 additions & 0 deletions templates/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,12 @@ en:
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
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:
invalid_class: |-
The specified guest class does not inherit from `Vagrant::Guest::Base`.
Expand Down Expand Up @@ -225,6 +231,13 @@ en:
vm_not_running: "VM is not currently running. Please bring it up to run this command."
box:
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:
success: |-
A `Vagrantfile` has been placed in this directory. You are now
Expand Down

0 comments on commit fbcb2c6

Please sign in to comment.