chruby icon indicating copy to clipboard operation
chruby copied to clipboard

Properly handle gems installed via --user-install

Open postmodern opened this issue 4 years ago • 8 comments

When a user installs a gem with gem install --user-install it installs the gem into the Gem.user_dir path (ex: ~/.gem/$RUBY_ENGINE/$RUBY_API_VERSION). chruby should probably support the "user installation directory", in order to prevent --user-installed gems from simply disappearing.

Simplest way to support --user-install is to add/remote the "user installation directory" to both $GEM_PATH and $PATH, in addition to the rubygems "gem root".

postmodern avatar Apr 11 '20 00:04 postmodern

It would mean a new env var (GEM_USER_DIR?), adding and removing it from PATH.

I'm not sure it's worth the trouble and it seems nobody cared so far, so probably this flag is almost never used. I detailed that a bit more in https://github.com/postmodern/chruby/pull/431#issuecomment-612017314

There is also the IMHO significant issue that supporting --user-install means we might again mix gem directories for different Rubies, which can lead to many unpleasant consequences (warnings from RubyGems and segfaults in some configurations), and would undo the "This guarantees a unique GEM_HOME/gem directories per installed Ruby" guarantee added in https://github.com/postmodern/chruby/pull/419

Considering that, and that I think no user asked for this in such a long time, I would prefer to not support --user-install, as it could cause more harm than help, and there is no reason (AFAIK) to use --user-install which chruby, it's unnecessary and dangerous/annoying (sharing gems from multiple Rubies in the same dir).

eregon avatar Apr 11 '20 11:04 eregon

Since Gem.user_dir is derived from ruby_engine and RbConfig::CONFIG['ruby_version'], we'd just need to add a RUBY_API_VERSION/RUBY_ENGINE_VERSION env variable to track the API version level. Then we could derive the string and add/remove it from both PATH and GEM_PATH.

I am leaning more towards supporting both the per-ruby (i.e. ruby-2.6.1) GEM_HOME and a fallback per-ruby-API-version (i.e. 2.6.0) "User Installation Directory". This would allow gem install --user-install to behave normally, and allow sharing some gems between patch-releases of rubies. Of course there are some issues with sharing gems between rubies (C extensions and explicit bin #!), however those can be worked around by running gem pristin, or simply just choosing not to use gem install --user-install if you don't want to risk it.

postmodern avatar Apr 14 '20 18:04 postmodern

Yeah, it's true the risk should be limited to people using --user-install. I would call Gem.user_dir though, there is no guarantee the path will not change in future RubyGems versions.

eregon avatar Apr 14 '20 20:04 eregon

There is also a non-trivial interaction with GEM_HOME/GEM_PATH, so Gem.user_dir + "/bin" should be only added to PATH if GEM_PATH is not set, to avoid duplicate entries in PATH.

eregon avatar Apr 14 '20 20:04 eregon

Actually, that's more complicated than I thought, even GEM_HOME/GEM_PATH don't change the USER INSTALLATION DIRECTORY, which in such a case is not even part of Gem.path and so won't be found by require!

$ GEM_HOME=a GEM_PATH=a gem env
...
  - USER INSTALLATION DIRECTORY: /home/eregon/.gem/ruby/2.6.0
...
  - GEM PATHS:
     - a
  - GEM CONFIGURATION:
...

$ GEM_HOME=a GEM_PATH=a ruby -e 'p Gem.path'     
["/pwd/a"]

$ GEM_HOME=a GEM_PATH=a gem i --user-install path
Fetching path-2.0.1.gem
WARNING:  You don't have /home/eregon/.gem/ruby/2.6.0/bin in your PATH,
	  gem executables will not run.
Successfully installed path-2.0.1
1 gem installed

$ GEM_HOME=a GEM_PATH=a ruby -e 'require "path"'
Traceback (most recent call last):
	2: from -e:1:in `<main>'
	1: from /.../rubygems/core_ext/kernel_require.rb:54:in `require'
/.../rubygems/core_ext/kernel_require.rb:54:in `require': cannot load such file -- path (LoadError)

Which makes me think --user-install is so brittle and such a hack that it doesn't seem worth supporting.

eregon avatar Apr 14 '20 20:04 eregon

@eregon Good call on pulling out the value of Gem.user_dir. The shell's hash function takes care of duplicate PATH entries. Also, you need to add Gem.user_dir to GEM_PATH in order to require the installed gems.

postmodern avatar Apr 14 '20 21:04 postmodern

Hmm I might push this back to post-1.0. Since chruby currently uses ~/.gem/$ruby_engine/X.Y.Z as the GEM_HOME, there's a possibility that a Gem.user_dir may overlap with a previous GEM_HOME.

postmodern avatar Apr 15 '20 23:04 postmodern

That makes sense to me, indeed chruby gems could mix with --user-install gems of Ruby 2.x.0 and nobody wants to debug that.

I think --user-install should be supported based on user demand. If nobody requested it since chruby exists then it seems nobody needs it. There are/will be probably more ways to configure RubyGems in weird ways, I'm not sure it's worth supporting them.

eregon avatar Apr 18 '20 12:04 eregon