Installing Vagrant plugins from local files

tl;dr: to install a Vagrant plugin locally before publishing it to Rubygems, use bundle cache and create a local rubygems index.

I have just bumped into a weird problem: I’m creating an Omnibus package, and I want to run builders for different systems with Vagrant. That’s supported and should run out of the box; but life would be too easy if it worked that way. There were some Gem updates, some dependency mess I’m too lazy to untangle, and as I tried to start vagrant up, I ran into an obscure backtrace (and I wasn’t the only person that saw this error). Seems like a new problem, and there’s already an issue open for it on vagrant-berkshelf project.

Upgrade to Berkshelf 2.0. With a bit of luck, it should be a one-liner. Clone, bundle, run tests; upgrade Berkshelf in .gemspec, bundle, run tests. They pass. Nice. Let’s try with my installed Vagrant… now, how do I install the patched plugin?

Let’s see…

Oh, well. According to Vagrant docs, *the* supported way to install plugins is by publishing them on Rubygems. I don’t want to publish it yet, I’ve just created an untested patch!

The vagrant plugin install command can accept file name. I suppose the file should be a built gem (the documentation doesn’t mention that), so let’s try. Sweet, now it fails to fetch the gem’s dependencies.

There also is a --plugin-source PLUGIN_SOURCE option, documented as Add a RubyGems repository source. If I generate a rubygems index for my single gem, it still fails to find dependencies. Looks like it rather means Use specified RubyGems repository source. I need all dependencies in place, together with my built gem. Bundler had such an option, I think…

Solution!

  • Build a .gem file with your plugin (for vagrant-berkshelf, it was thor gem:build).
  • Run bundle cache to have all dependencies as .gem files in vendor/cache directory.
  • Copy built pkg/vagrant-berkshelf-1.2.0.gem gem to vendor/cache
  • cd vendor/cache
  • Build a RubyGems repository index: gem generate_index
  • Install the plugin out of local RubyGems repository: /usr/bin/vagrant plugin install vagrant-berkshelf --plugin-source file://pwd

I needed to run /usr/bin/vagrant explicitly, because I let Bundler install Vagrant as a local Ruby gem and put it in my path before system-wide Vagrant. If you do bundle install --path=somewhere/else (I usually go with vendor/gems), you won’t need to do this.

I hope this will save someone else an hour or two of their life, or even a gray hair – maybe it will be myself a month from now, trying to remember what was the magic there!