devcontainers / devcontainers/features

Install only one of `rvm` or `rbenv` for managing ruby versions

Open
#603 5 comments 2 reactions 0 assignees View on GitHub
Dominant language
Shell
Stars
1.5k
Forks
621
Avg merge
2d 11h
Merged PRs (30d)
4

Description

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.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.