mitchellh/vagrant — pull request #2769 by mitchellh (2014-01-06 19:07:21+00:00)
Title: Plugin dependency management revamp

This is a huge revamp of how plugin dependency management is done. To understand the changes here, a brief history lesson is in order:

Since Vagrant 1.1, plugins have been loaded as RubyGems. Once Vagrant was loaded, it would iterate through a list of installed plugins, and `require` that plugin. This mostly worked okay. But the devil is in the details, and the edge cases were _really_ bad. In addition to the edge cases (mentioned below), building things like updaters, version constraints (">= 1.0", "< 1.1"), etc. all had to be done manually. This seemed silly, since RubyGems itself (and Bundler) do these sort of things for you. Why reinvent the wheel?

As for edge cases: the primary edge case is that since the dependencies of Vagrant and its respective plugins weren't resolved as a whole, you can run into cases where plugin installation succeeded, but plugin loading failed because Vagrant already loaded a common dependency with the wrong version. An example explains this best:

* Vagrant depends on "A >= 1.0, < 1.2"
* vagrant-plugin depends on "A = 1.1"
* When you run Vagrant, it loads the latest possible matching dependencies, so it would load A 1.2
* When Vagrant loads vagrant-plugin, it can't load, because A 1.2 is active, so A 1.1 can't be loaded.

The error above should never happen: the versions available for A should satisfy both Vagrant and vagrant-plugin (by loading v1.1 for both).

With this new branch, all plugin installation, dependency resolution, updating, etc. is managed by [Bundler](http://gembundler.com). This has yielded numerous benefits:

* Vagrant now resolves dependencies before Vagrant is even loaded. This ensures that all plugins will be able to load. No more conflicts at run-time.

* Conflicts are detected at `vagrant plugin install` time. This means that if there would be a crash if that plugin were to load, the plugin won't even install and a human-friendly error is shown to the end user.

* `vagrant plugin install` now accepts complex version constraints such as "~> 1.0.0" or ">= 1.0, < 1.1". Vagrant stores these constraints for updating, which leads to the next point.

* `vagrant plugin update` without arguments now updates all installed plugins, respecting the constraints specified by `vagrant plugin install`.

* `vagrant plugin update NAME` will only update that gem (still respecting constraints).

* Internally, there are a lot more unit tests. /cc @phinze :)

The goal of this branch was to replace the _existing_ system and functionality with Bundler-ized management. It did not introduce any new features except where they naturally fell into place (version constraints). However, with this new system, many new possibilities are also available:

* Vagrant environment local plugins (i.e. a Gemfile but for a specific Vagrant environment).

* Plugin installation from git

I'm sure those will be pursued at some point in the future.

This fixes: #2612, #2406, #2428