Repository navigation
Bundler plugins #3319
Description
Bundler Plugin System
The Bundler Plugin system allows a developer to integrate into certain points in Bundler's runtime. These include, at the time of writing, before-install, before-install-all, after-install, and after-install-all. There is also an option to add your own bundle commands like bundle viz.
Each of these points allows a plugin to interact with a Bundler::ParallelInstaller::SpecInstallation in the singular install options, and arrays of Bundler::Dependency objects in the -all options.
These provide users with the opportunities to modify the behaviour of bundle install to their heart's content.
I've seen two use cases in practice:
- https://github.com/jules2689/extended_bundler-errors : A
bundlerplugin that makes gem installation errors more actionable, educative, and all around easier to understand - https://github.com/shopify/bootboot: Bootboot is a Bundler plugin meant to help dual boot your ruby application.
The former was created by myself and is intended for a large audience (everyone), while the latter was created by Rafael Franca and Edouard Chin - of the Shopify Rails team - and is intended for an organization or project needing to run two versions of a gem at once.
Distribution
The 2 aforementioned plugins presents two different distribution systems. The first errors handling plugin is intended to be installed using bundler plugin install while the latter is intended to be listed in the Gemfile using the plugin "blah" stanza. Both methods have issues that I'll enumerate in this issue.
bundler plugin install
The issue here is that it assumes users know to run bundle plugin install. This is not muscle memory, particularly since there exists practically no plugin gems available for use.
Gemfile's plugin stanza
Once a Gemfile lists plugin in the stanza, it now has to evaluate every single time. bundle install causes the plugin to be install every single time. Likely because it is not added to the .bundle/plugin/index file.
A Common Problem
What plugins exist? In what ways can I extend Bundler? No one can answer these questions. It is up to the plugin developer to market their plugin gem, to little benefit. Many people have never heard of Plugins.
This is even listed here: https://github.com/bundler/bundler/issues/4682
Issues with the plugin index
- The plugin index lists all gems with a full path (https://github.com/bundler/bundler/issues/6943, partial fix in a Ruby Buildpack for Heroku is here: https://github.com/intercom/heroku-buildpack-ruby/pull/4).
- The plugin index can be located at multiple locations (as is normal with bundler), either in
./.bundle/plugins/indexor~/.bundle/plugin/index- it's not obvious what is where though - The plugin index has an unbounded growth in the hooks section. Likewise if you install multiple versions of the same plugin, it does not get overwritten, it's simply appended.
commands: hooks: after-install: - "extended_bundler-errors" - "extended_bundler-errors" - "extended_bundler-errors" before-install-all: - "extended_bundler-errors" - "extended_bundler-errors" - "extended_bundler-errors" load_paths: extended_bundler-errors: - "/Users/juliannadeau/.bundle/plugin/gems/extended_bundler-errors-0.3.2/lib" - "/Users/juliannadeau/.bundle/plugin/gems/extended_bundler-errors-0.3.1/lib" plugin_paths: extended_bundler-errors: "/Users/juliannadeau/.bundle/plugin/gems/extended_bundler-errors-0.3.2" sources:
- I've had users experience plugins being deleted at the path specified in the
index, to which Bundler seems to have issues
Developing/Managing Plugins
- Paths do not work in the
bundle plugin installcommand, but once installed in another way, it ends up in the index forever and just works (TM)~/src/github.com/jules2689/website(master*) ➜ bundle plugin install /Users/juliannadeau/src/github.com/jules2689/extended_bundler-errors Fetching gem metadata from https://rubygems.org/. Fetching gem metadata from https://rubygems.org/. Could not find gem '/Users/juliannadeau/src/github.com/jules2689/extended_bundler-errors' in any of the gem sources listed in your Gemfile. ~/src/github.com/jules2689/website(master*) ➜ bundle plugin install extended_bundler-errors Fetching gem metadata from https://rubygems.org/. Resolving dependencies... Using bundler 1.17.2 Installing extended_bundler-errors 0.3.2 Installed plugin extended_bundler-errors - You cannot uninstall plugins once installed (https://github.com/bundler/bundler/issues/5447)
- There are no way to manage or list plugins (Add
bundle plugin listcommand rubygems/bundler#5467) despite alistcommand being added (Add plugin list command rubygems/bundler#6120), it does not work.~ ➜ bundle -v Bundler version 2.0.1 ~ ➜ bundle plugin list Could not find command "list".
Summary
There are 3 main categories to focus on:
- Technical Issues
- Distribution
- Management of Plugins
These 3 categories cause plugins to be more difficult to develop than needed (installing local plugins), difficult to use (technical issues and management), and difficult to find (distribution). This section aims to provide a list of requirements to start to remedy these issues.
Technical Issues
- Fix the full path issue in the plugin index
- Implement a more coherent way to handle the
pluginindex, that is apparent and evident. Tell the user when a plugin is being installed that a plugin is "Installing globally" or "Installing locally"pluginstanza goes to.bundle/plugins/indexbundle plugin installgoes to~/.bundle/plugins/indexunless--localflag or something is set
- Make sure the index is not every growing in the hooks section
- Make sure that upgrading a plugin does not cause 2 entries of the same plugin (de-dupe the plugin paths)
- Make sure that the
pluginstanza does not cause abundle installto always evaluate and does not prevent abundle checkfrom succeeding
Distribution
- On Rubygems.org, list a section for
plugins. This can be determined by looking for a validplugin.rbfile in the base of the gem. - Once that is determined, then allow users to search for the plugins and present the gem in a different show page than a standard gem
I don't expect wide adoption of plugins. It is likely to be a fragmented system, so we should be looking to see how a plugin affects the ecosystem and adopt the ones intended for all users into core where it makes sense.
Management of plugins
- Implement a
bundle plugin uninstallcommand - Implement, properly, a
bundle plugin listcommand - Make sure users do not have to touch the
indexfile - Allow path installs in the
bundle plugin install(this should be local only, I'd think?)
Activity
jules2689 commentedon Apr 30, 2019
cc @indirect @segiddins @colby-swandale
Here is the plugin write up I finally sat down and wrote.
segiddins commentedon May 12, 2019
cc @asutoshpalai who implemented plugins
indirect commentedon May 21, 2019
@jules2689 thanks! this is super helpful. Would you be interested in writing up an RFC-format document with what you think the next version of plugins should look like? If not, no worries, and we'll try to work on improving the plugin system as we have time.
sergey-alekseev commentedon Oct 5, 2019
Could a plugin be installed only for a specific group? E.g.
plugin 'my_plugin', group: :developmentorUPD: if I place a plugin under the development group and run
bundle install --without=developmentI get the following error:Bundler version 1.17.3.
ccutrer commentedon Sep 14, 2023
I've been looking this, and it seems several of the issues mentioned here have been fixed:
bundle plugin listworksbundle plugin uninstallworksbundle plugin install --local_gitworks if your gem is in a repo directoryThese things are still broken:
gem uninstall bundler-multilock -v 1.0.3when the plugin index refers to the global gem can break the bundle until you runbundle plugin uninstall bundler-multilock. However, you have to be careful, because ofbundle plugin uninstallwipes out your code if using a path #6953bundle plugin install --pathor equivalent (Allow installing plugins from path via CLI #6960)I'd also like to see a new hook for before/after gemfile evaluation. It seems like a lot of plugins recommend doing
Plugin.send(:load_plugin, ...)in the Gemfile so they can extend the DSL in some way, and before_gemfile_eval hook would allow them to no longer need the awkward load. (#6961)ccutrer commentedon Sep 14, 2023
#6957 now fixes this as well
ccutrer commentedon Sep 15, 2023
#6957 also now gracefully handles if a plugin is referencing a gem outside the .bundle directory, and that gem gets removed. It gives a nice error message
Plugin(s) bundler-multilock (= 1.0.4) are not installed Run `bundle install` to install missing gems.rather than an obscure stack trace.hsbt commentedon Jan 26, 2026
The plugin system described in this issue has already been implemented. Since I don't understand the intention behind creating the issue, I'm closing this.
There's still room for improvement in Bundler's plugin system, so we'd like to discuss those separately and move forward like #6957 and #6961.