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 rming 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:
|
"GEM_PATH": "/usr/local/rvm/gems/default:/usr/local/rvm/gems/default@global", |
|
"GEM_HOME": "/usr/local/rvm/gems/default", |
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.
Is there any benefit to installing both rvm and rbenv?
Context
rvmdoes so much: it installs plugins that change the behavior of rubygems and thegemcommand, it overrides and updates a bunch of environment variables, it updatesumask, it edits therubybinary, and it performs various other mutations on your environment whenever you load your shell or change directories.Unfortunately, if you load
rbenvor place its shims earlier in the PATH thanrvm, then some truly weird situations arise. E.g:which gemreports the rbenv shim, but runninggemuses 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
rvmdocumentation for how to uninstallrvmsimply doesn't work inside devcontainers that use the ruby feature. After the rvm files have been removed and various/etcfiles have been edited, the rbenv installation might still be broken because thedevcontainer-feature.jsonsets theGEM_HOMEandGEM_PATHenvironment variables to rvm directories.In situations (such as GitHub Codespaces) where the default image has both
rvmandrbenvinstalled, this can run into trouble with user's dotfiles, which are often configured to check forrbenvand prefer it overrvm.Workarounds
Unfortunately, setting
GEM_HOMEandGEM_PATHto empty strings is not the same as unsetting them. And it isn't possible to simply unset Dockerfile ENV vars. (Is it possible to unsetdevcontainer.jsonenv vars?)We can manually override those variables to something else in our own devcontainer.json files, but that still effectively breaks
rbenv:rbenvuses shims to load the correct versions ofrubyorgem, 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 setsRBENV_VERSION, when using multiple repositories, or when multiple directories in a single repository have different .ruby-version files. SettingGEM_HOMEandGEM_PATHto 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 rming all of the rvm dirs and then callunset GEM_HOME; unset GEM_PATHfrom my shell prompt.Various proposals
The simplest approach is probably to simply not install
rbenvinside 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,
rbenvis more popular thanrvm. It is also simpler and easier to debug. Personally, I'd rather seerbenvpromoted as the default. Users should still be able to installrvmand I think it should still work, more or less.rvmbreaksrbenvbutrbenvdoesn't breakrvm. 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:features/src/ruby/devcontainer-feature.json
Lines 29 to 30 in 1869e59
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
rvmis 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 forchruby: https://rvm.io/workflow/chruby. This usesmrvmto install ruby versions andchrubyto switch. I believe thismrvmapproach could also be made to work withrbenv, 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.