Skip to content
This repository has been archived by the owner on Apr 14, 2021. It is now read-only.

Commit

Permalink
Auto merge of #6024 - gwerbin:custom-user-bundle-dir, r=colby-swandale
Browse files Browse the repository at this point in the history
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 #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 #5787 (comment)), 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.

(cherry picked from commit e6cc7a2)
  • Loading branch information
bundlerbot authored and colby-swandale committed Oct 9, 2018
1 parent af1e58a commit aad374a
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 10 deletions.
26 changes: 20 additions & 6 deletions lib/bundler.rb
Expand Up @@ -195,8 +195,26 @@ def tmp_home_path(login, warning)
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
Expand All @@ -211,10 +229,6 @@ def specs_path
bundle_path.join("specifications")
end

def user_cache
user_bundle_path.join("cache")
end

def root
@root ||= begin
SharedHelpers.root
Expand Down
6 changes: 3 additions & 3 deletions lib/bundler/plugin.rb
Expand Up @@ -81,8 +81,8 @@ def index

# 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
Expand All @@ -97,7 +97,7 @@ def local_root

# 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
Expand Down
2 changes: 1 addition & 1 deletion lib/bundler/settings.rb
Expand Up @@ -395,7 +395,7 @@ def global_config_file
Pathname.new(ENV["BUNDLE_CONFIG"])
else
begin
Bundler.user_bundle_path.join("config")
Bundler.user_bundle_path("config")
rescue PermissionError, GenericSystemCallError
nil
end
Expand Down
58 changes: 58 additions & 0 deletions spec/bundler/bundler_spec.rb
Expand Up @@ -306,4 +306,62 @@
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

0 comments on commit aad374a

Please sign in to comment.