features
features copied to clipboard
Install only one of `rvm` or `rbenv` for managing ruby versions
Is there any benefit to installing both rvm and rbenv?
Context
rvm
does so much: it installs plugins that change the behavior of rubygems and the gem
command, it overrides and updates a bunch of environment variables, it updates umask
, it edits the ruby
binary, and it performs various other mutations on your environment whenever you load your shell or change directories.
Unfortunately, if you load rbenv
or place its shims earlier in the PATH than rvm
, then some truly weird situations arise. E.g: which gem
reports the rbenv shim, but running gem
uses the rvm installation. And rvm can "infect" the rbenv installation with its own gem plugins, and other similar clashes. This creates an unstable system with arcane error messages that can be incredibly difficult and frustrating to debug.
Even worse, the rvm
documentation for how to uninstall rvm
simply doesn't work inside devcontainers that use the ruby feature. After the rvm files have been removed and various /etc
files have been edited, the rbenv installation might still be broken because the devcontainer-feature.json
sets the GEM_HOME
and GEM_PATH
environment variables to rvm directories.
In situations (such as GitHub Codespaces) where the default image has both rvm
and rbenv
installed, this can run into trouble with user's dotfiles, which are often configured to check for rbenv
and prefer it over rvm
.
Workarounds
Unfortunately, setting GEM_HOME
and GEM_PATH
to empty strings is not the same as unsetting them. And it isn't possible to simply unset Dockerfile ENV vars. (Is it possible to unset devcontainer.json
env vars?)
We can manually override those variables to something else in our own devcontainer.json files, but that still effectively breaks rbenv
: rbenv
uses shims to load the correct versions of ruby
or gem
, and those installed versions provide their own sensible dynamic defaults. This is especially useful when switching between branches with different ruby versions, when testing multiple ruby versions with a shell script that sets RBENV_VERSION
, when using multiple repositories, or when multiple directories in a single repository have different .ruby-version files. Setting GEM_HOME
and GEM_PATH
to a static path can break all of these scenarios.
I often run into this problem on public GitHub Codespaces. My most common workaround is to simply work locally and not use a devcontainer or the Codespace. If I'm just doing a quick fix, I'll remove rbenv from my dotfiles. But when I'm using the devenv for a little bit longer, I'll workaround by first sudo rm
ing all of the rvm dirs and then call unset GEM_HOME; unset GEM_PATH
from my shell prompt.
Various proposals
The simplest approach is probably to simply not install rbenv
inside the ruby feature. It's already broken, so removing it would stop wasting people's time with long, confusing, disappointing debugging sessions.
However, both anecdotally and based on github stars and forks, rbenv
is more popular than rvm
. It is also simpler and easier to debug. Personally, I'd rather see rbenv
promoted as the default. Users should still be able to install rvm
and I think it should still work, more or less. rvm
breaks rbenv
but rbenv
doesn't break rvm
. However this might not be considered backward compatible, and probably requires a major version bump.
IMO, the most important thing would be to simply remove the following lines from devcontainer-feature.json
: https://github.com/devcontainers/features/blob/1869e5931cfe0517f75d58cb70863a6b4874c487/src/ruby/devcontainer-feature.json#L29-L30
Then, if a user wants to disable rvm
, all they would need to do is delete or comment out /etc/profile.d/rvm.sh
. And I think that a manually installed rvm could still work, so long as everything is able to load the environment from /etc/profile.d/rvm.sh
.
There might be some reason that rvm
is strongly preferred, e.g: installations are faster because it downloads binaries and bit-twiddles them, rather than compile them. In that case, it might be reasonable to use the "mini rvm" integration that is available for chruby
: https://rvm.io/workflow/chruby. This uses mrvm
to install ruby versions and chruby
to switch. I believe this mrvm
approach could also be made to work with rbenv
, although I've never attempted this myself.
If any of these proposals sound like they might be acceptable, or if you have another better alternative in mind, I can create a draft PR for you to review.