diff options
author | The Bundler Bot <bot@bundler.io> | 2017-09-18 04:26:44 +0000 |
---|---|---|
committer | The Bundler Bot <bot@bundler.io> | 2017-09-18 04:26:44 +0000 |
commit | e6cc7a24d5ca9da842aeefd23167be4d850c9aa5 (patch) | |
tree | b4708edbae7386963b66f7cbe9d6d17d7f93c846 | |
parent | 4fc8fe968ff61ece41f5a74ebc117f84cf22f827 (diff) | |
parent | b3108fac3f9ec1a85554790b9401ade370392b75 (diff) | |
download | bundler-e6cc7a24d5ca9da842aeefd23167be4d850c9aa5.tar.gz |
Auto merge of #6024 - gwerbin:custom-user-bundle-dir, r=colby-swandale
Allow user to override ~/.bundle with environment variables
### What was the end-user problem that led to this PR?
As in #4333, users wanted a way to make Bundler respect the XDG specification.
### What was your diagnosis of the problem?
The directory `~/.bundle` was hard-coded and could not be configured.
### What is your fix for the problem, implemented in this PR?
Allow users to configure `~/.bundle` and its relevant sub-directories by setting environment variables. The environment variables and their fallbacks are as follows:
| variable | fallback if unset |
|---|---|
| `BUNDLE_USER_HOME` | `$HOME/.bundle` |
| `BUNDLE_USER_CACHE` | `$BUNDLE_USER_HOME/cache` |
| `BUNDLE_USER_CONFIG` | `$BUNDLE_USER_HOME/config` |
| `BUNDLE_USER_PLUGIN` | `$BUNDLE_USER_HOME/plugin` |
### Why did you choose this fix out of the possible options?
Unlike https://github.com/bundler/bundler/pull/5787, This solution is not specific to the XDG specification. Users have all kinds of setups, and this is a very general system for allowing them to configure their development machines however they need. It tries to keep all files created by Bundler in the same place (as per https://github.com/bundler/bundler/pull/5787#issuecomment-310154273), but allows the user to override that convention _if they really want to and they know what they are doing_.
If they want to use XDG for everything, they can do it by explicitly setting the `BUNDLE_USER_*` variables to the equivalent `XDG_DATA_*`. If they just want to get `.bundle` out of their home directory, they can do it by setting `BUNDLE_USER_HOME` and don't have to mess with the more specific env variables if they don't want to.
To me, this solution strikes the right balance between "fine-grained control for power users" and "simple, sane defaults for everyone else".
Please let me know if my tests can be improved. My only Ruby experience so far has been writing Homebrew formulas and configuring Jekyll, so I'm sure I have a lot to learn.
-rw-r--r-- | lib/bundler.rb | 26 | ||||
-rw-r--r-- | lib/bundler/plugin.rb | 6 | ||||
-rw-r--r-- | lib/bundler/settings.rb | 2 | ||||
-rw-r--r-- | spec/bundler/bundler_spec.rb | 58 |
4 files changed, 82 insertions, 10 deletions
diff --git a/lib/bundler.rb b/lib/bundler.rb index 81c6a5b594..6c942baf59 100644 --- a/lib/bundler.rb +++ b/lib/bundler.rb @@ -193,8 +193,26 @@ module Bundler raise e.exception("#{warning}\nBundler also failed to create a temporary home directory at `#{path}':\n#{e}") end - def user_bundle_path - Pathname.new(user_home).join(".bundle") + def user_bundle_path(dir = "home") + env_var, fallback = case dir + when "home" + ["BUNDLE_USER_HOME", Pathname.new(user_home).join(".bundle")] + when "cache" + ["BUNDLE_USER_CACHE", user_bundle_path.join("cache")] + when "config" + ["BUNDLE_USER_CONFIG", user_bundle_path.join("config")] + when "plugin" + ["BUNDLE_USER_PLUGIN", user_bundle_path.join("plugin")] + else + raise BundlerError, "Unknown user path requested: #{dir}" + end + # `fallback` will already be a Pathname, but Pathname.new() is + # idempotent so it's OK + Pathname.new(ENV.fetch(env_var, fallback)) + end + + def user_cache + user_bundle_path("cache") end def home @@ -209,10 +227,6 @@ module Bundler bundle_path.join("specifications") end - def user_cache - user_bundle_path.join("cache") - end - def root @root ||= begin SharedHelpers.root diff --git a/lib/bundler/plugin.rb b/lib/bundler/plugin.rb index 99c9a867b0..7db159c593 100644 --- a/lib/bundler/plugin.rb +++ b/lib/bundler/plugin.rb @@ -80,8 +80,8 @@ module Bundler # The directory root for all plugin related data # - # Points to root in app_config_path if ran in an app else points to the one - # in user_bundle_path + # If run in an app, points to local root, in app_config_path + # Otherwise, points to global root, in Bundler.user_bundle_path("plugin") def root @root ||= if SharedHelpers.in_bundle? local_root @@ -96,7 +96,7 @@ module Bundler # The global directory root for all plugin related data def global_root - Bundler.user_bundle_path.join("plugin") + Bundler.user_bundle_path("plugin") end # The cache directory for plugin stuffs diff --git a/lib/bundler/settings.rb b/lib/bundler/settings.rb index d9daba43f6..75e2d4dff2 100644 --- a/lib/bundler/settings.rb +++ b/lib/bundler/settings.rb @@ -393,7 +393,7 @@ module Bundler Pathname.new(ENV["BUNDLE_CONFIG"]) else begin - Bundler.user_bundle_path.join("config") + Bundler.user_bundle_path("config") rescue PermissionError, GenericSystemCallError nil end diff --git a/spec/bundler/bundler_spec.rb b/spec/bundler/bundler_spec.rb index 19e3f0336f..94d4096cd3 100644 --- a/spec/bundler/bundler_spec.rb +++ b/spec/bundler/bundler_spec.rb @@ -227,4 +227,62 @@ EOF expect(Bundler.tmp_home_path("USER", "")).to eq(Pathname("/TMP/bundler/home/USER")) end end + + context "user cache dir" do + let(:home_path) { Pathname.new(ENV["HOME"]) } + + let(:xdg_data_home) { home_path.join(".local") } + let(:xdg_cache_home) { home_path.join(".cache") } + let(:xdg_config_home) { home_path.join(".config") } + + let(:bundle_user_home_default) { home_path.join(".bundle") } + let(:bundle_user_home_custom) { xdg_data_home.join("bundle") } + + let(:bundle_user_cache_default) { bundle_user_home_default.join("cache") } + let(:bundle_user_cache_custom) { xdg_cache_home.join("bundle") } + + let(:bundle_user_config_default) { bundle_user_home_default.join("config") } + let(:bundle_user_config_custom) { xdg_config_home.join("bundle") } + + let(:bundle_user_plugin_default) { bundle_user_home_default.join("plugin") } + let(:bundle_user_plugin_custom) { xdg_data_home.join("bundle").join("plugin") } + + describe "#user_bundle_path" do + before do + allow(Bundler.rubygems).to receive(:user_home).and_return(home_path) + end + + it "should use the default home path" do + expect(Bundler.user_bundle_path).to eq(bundle_user_home_default) + expect(Bundler.user_bundle_path("home")).to eq(bundle_user_home_default) + expect(Bundler.user_bundle_path("cache")).to eq(bundle_user_cache_default) + expect(Bundler.user_cache).to eq(bundle_user_cache_default) + expect(Bundler.user_bundle_path("config")).to eq(bundle_user_config_default) + expect(Bundler.user_bundle_path("plugin")).to eq(bundle_user_plugin_default) + end + + it "should use custom home path as root for other paths" do + ENV["BUNDLE_USER_HOME"] = bundle_user_home_custom.to_s + expect(Bundler.user_bundle_path).to eq(bundle_user_home_custom) + expect(Bundler.user_bundle_path("home")).to eq(bundle_user_home_custom) + expect(Bundler.user_bundle_path("cache")).to eq(bundle_user_home_custom.join("cache")) + expect(Bundler.user_cache).to eq(bundle_user_home_custom.join("cache")) + expect(Bundler.user_bundle_path("config")).to eq(bundle_user_home_custom.join("config")) + expect(Bundler.user_bundle_path("plugin")).to eq(bundle_user_home_custom.join("plugin")) + end + + it "should use all custom paths, except home" do + ENV.delete("BUNDLE_USER_HOME") + ENV["BUNDLE_USER_CACHE"] = bundle_user_cache_custom.to_s + ENV["BUNDLE_USER_CONFIG"] = bundle_user_config_custom.to_s + ENV["BUNDLE_USER_PLUGIN"] = bundle_user_plugin_custom.to_s + expect(Bundler.user_bundle_path).to eq(bundle_user_home_default) + expect(Bundler.user_bundle_path("home")).to eq(bundle_user_home_default) + expect(Bundler.user_bundle_path("cache")).to eq(bundle_user_cache_custom) + expect(Bundler.user_cache).to eq(bundle_user_cache_custom) + expect(Bundler.user_bundle_path("config")).to eq(bundle_user_config_custom) + expect(Bundler.user_bundle_path("plugin")).to eq(bundle_user_plugin_custom) + end + end + end end |