All-in-one tooling for Ruby developers.
- Manage Ruby versions, gems, applications, tools, and scripts, effortlessly.
- Handle everything Ruby provided by
rvm,rbenv,chruby,asdf,mise,ruby-build,ruby-install,bundler, andrubygems, all at once. - Install Ruby and all your gems in seconds, without having to compile anything ever again.
- Run any gem command instantly, like
rvx rails new. - Run any script, installing all needed gems, like
rv run script.rb. - Install gem CLIs with any needed rubies with
rv tool install. - Install precompiled Ruby versions in seconds with
rv ruby install. - Manage project gems with
rv sync,rv add, andrv remove. - Create gems with
rv gem, and publish them withrv publish.
rv combines several functions that have previously been separate tools:
- ruby version manager (like
rvm,rbenv,chruby) - ruby version installer (like
ruby-build,ruby-install) - gem installer (like
rubygems) - dependency installer (like
bundler) - project runner (like
npm,make,rake) - package dev & publishing (like
rubygems&bundler) - running packages (like
gem execornpm exec) - installing tools (like
uv tool, ruby and node lack this today)
-
rv run CMD -
rv ruby install -
rv ruby list -
rv ruby pin -
rv ruby dir -
rv ruby uninstall -
rv ruby eol
-
rvx/rv tool run -
rv tool install -
rv tool uninstall
-
rv clean-install/rv ci -
rv sync -
rv run [TASK] -
rv list [GEM] -
rv upgrade [--all|GEM] -
rv add GEM -
rv remove GEM -
rv tree [GEM] -
rv eol -
rv info GEM -
rv search NAME -
rv new DIR -
rv add --script FILE GEM -
rv remove --script FILE GEM
-
rv gem NAME -
rv build -
rv publish [SERVER]
-
rv shell zsh -
rv shell bash -
rv shell fish -
rv shell nushell -
rv shell powershell
-
rv shell init -
rv shell env -
rv shell completions
- MRI HEAD
- MRI 4.0
- MRI 3.4
- MRI 3.3
- MRI 3.2
- JRuby 10
- TruffleRuby 24
- MRI 3.1
- MRI 3.0
- MRI 2.7.8
- MRI 2.5.9
- MRI 2.3.8
- MRI 1.9.3
- MRI 1.8.7
- MRI 1.2.6
- MRI 1.0
- MRI 0.49
- macOS 15+
- Linux (glibc 2.35+)
- Alpine (musl 1.2.5+)
- Windows 11+
rv can be configured globally, per user, and per project, as well as via ENV. configuration files use the KDL document format.
settings we know we want to support include:
- ruby location(s)
- ruby installation location
- default ruby version
Ruby version can be set by .ruby-version (shared with rvm, rbenv, chruby) or .tool-versions (shared with asdf, and mise).
User-wide settings are located in ~/.config/rv/config.kdl or ~/.rv.kdl.
Project settings are located in gem.kdl at the root of the project directory.
Projects are libraries, or packages, or applications. A project might be inside a workspace that aggregates together several projects.
A project root may be indicated by a Gemfile or a gem.kdl config file.
Most rv commands (like add, remove, install, and lock) are scoped to a project and that project's dependencies. Some commands also interact with the user or global state, like ruby install, tool install, etc.
Projects can be one or more of:
- application (like a Rails or Hanami app)
- gem (library for publishing)
- library (structured like a gem but not published)
We may want to provide explicit support for libraries vs gems, although today Bundler treats them as the same.
Applications typically have their own framework generators, so it's unlikely we will need to build any application-specific functionality. On the other hand, gems and libraries are typically generated using bundle gem so we will likely want to support generation for that. On the third hand, we may even want to offer an extremely fast templating and generator framework that applications could use instead of Thor.
Workspaces are a group of projects that all have their own dependencies, but are resolved and locked together as a group to ensure that one set of gems will work across all of the packages. In Bundler, this is handled via path gems, so we could likely do something similar with a parent gem.kdl that points to child gem/library folders.
Supporting workspaces specifically means not just a parent project, but resolving dependencies for each child gem during rv install or rv lock scoped to the parent workspace, to guarantee every child project uses the same single set of versions resolved for the parent. The effect of commands like rv add will remain scoped to a single project (and the closest gem.kdl), but they will need to re-resolve the workspace parent gem.kdl.
A ruby version file (named .ruby-version) indicates the desired/locked Ruby version for a specific project or workspace. Any project intended to be usable by itself, outside of a workspace context, should declare a Ruby version.
The ruby version can also be provided by a .tool-versions file, shared by tools like asdf and mise.
Finally, the ruby version can also be provided by gem.kdl, although tools other than rv may not be able to read that.
The main location for Rubies is $XDG_DATA_HOME/rv/rubies. We also look for Rubies in locations like ~/.rubies, /opt/rubies, /opt/homebrew/Cellar/ruby/, /usr/local/rubies, and /usr/local/Cellar/ruby.
The default location for $XDG_DATA_HOME is ~/.local/share, so we install rubies into ~/.local/share/rv/rubies by default.
While Bundler invented the idea of bundle exec to run commands in the context of a bundle, both node and python have settled on different terminology: run NAME is for running commands inside a bundle, while exec NAME is for running a package that might not even be installed yet, without adding the package to the current bundle.
In uv, exec is so common that it has a dedicated binary: uvx. This mirrors the npm shortcut for npm exec, which is named npx. That implies we should provide our own shortcut binary named rvx.
Tools are executables provided by a package, installed in a fully isolated environment with their own Ruby version and gemset, and run directly via a command placed directly on the PATH. For example, you could run rv tool install gist to end up with a gist command that works correctly whether you are inside or outside a Gemfile directory, inside or outside bundle exec, or even regardless of whether you have Ruby installed at all when you first run tool install.
This is roughly the equivalent of cargo install or go install, but taking care of the additional concerns raised by needing an interpreter. uv tool install handles all of this extremely well for Python, but Ruby has never had a tool that does this.
Notes about the functionality and implementation of each command.
The ruby subcommand manages ruby versions, using subcommands install, uninstall, pin, and find.
The install command downloads a precompiled ruby for the current architecture and operating system, installing it into the rubies directory (which defaults to ~/.local/share/rv/rubies).
Pin with no arguments reports the project's currently chosen version of Ruby.
Pin with a version argument tries to set that version for the current project, validating the version, installing the version if needed, and then writing the version into the current project's .ruby-version file.
The ruby find subcommand returns the full path to the currently chosen Ruby interpreter. If passed an argument, it interprets that argument as a version request and prints the full path to a Ruby interpreter that satisfies the version request.
Set up an existing Ruby project to work with rv. Create a gem.kdl file, import supported settings from .bundle/config, import dependencies from Gemfile, import package configuration from *.gemspec, and print some instructions for anything else that needs to be done manually. After running rv init, all the other commands (like ci, run, add, etc) are functional.
The clean-install or ci command is mainly inspired by npm ci, and is functionally very similar to bundle install --frozen. It installs dependencies as described by the lockfile, and does not interact with the Gemfile.
The run command executes commands and files provided by the current project or filesystem. Contrast to exec, below, which executes commands provided by installing gems. There are several sources of commands for run: 1) the $PATH, 2) your project, 3) a file
-
Anything in your
$PATHcan be run byrv run, includingruby! In practice, this sets up a Ruby environment and then runs your command. For examplerv run --ruby 3.4.2 -- rubywill run ruby 3.4.2 regardless of which ruby version is currently activated by the environment and the current project. Similarly, if you runrv run bashand then try to runruby, you will get the Ruby version configured by rv. -
Your project can provide commands for
runin two ways: 1) by defining named commands in thegem.kdlconfiguration file, likenpm runwithpackage.json, or 2) declaring your project provides binaries, like thegistgem provides thegistcommand. If you are working on thegistgem, you canrv run gistto run the current project's command. -
You can write a Ruby script and then run it with
rv run script.rb. Scripts can optionally contain their own required ruby versions and rubygems dependencies, as a magic comment with the same structure asgem.kdl. If the script has configuration comments setting a required ruby version or depending on gems, rv will install that ruby version and those gems and then run thes cript. If the script does not declare any Ruby or gem dependencies, rv will simply ensure a Ruby is installed and use it to run the script.
The shell subcommand handles integration with the user's shell, including automatic ruby version switching and completions for rv commands.
Passing the name of a shell, as in rv shell zsh, prints out instructions for configuring that shell with rv's ruby version management and CLI completions.
init (hidden)
The init command prints out shellscript that sets up automatic version switching, and is intended to be used like eval "$(rv shell init zsh)" to set up zsh to then automatically run eval "$(rv shell env)" every time the user changes directories, which provides automatic version switching.
env (hidden)
The env command prints out the env vars that need to be set for the currently-desired ruby version, like RUBY_VERSION and PATH. The output is expected to be evaled by the shell to change the installation that will run as ruby.
completions (hidden)
The completions command prints out shell-specific output that can be evaled to set up tab-completion for subcommands and arguments to commands.
The tool subcommand manages binaries available on the PATH, ensuring that a usable Ruby is installed, the gem and all of its dependencies are installed, and a binary is created and put somewhere in the PATH. The binary needs to ignore the currently chosen ruby version, the current bundle environment, and anything else necessary to ensure that when it is invoked it will run completely independently.
The tool run command runs commands provided by gems. Contrast with run, above, which executes commands provided by the current project or filesystem.
Just like npm exec, uvx, and gem exec, rv exec will: find a package with the given name, install it, and run the executable inside that package with the same name. For example, rv exec rails new . will install ruby if needed, install rails if needed, and then run rails new ..
Similar to npm and uv, rvx rails@latest new . will check for the newest version of Rails and make sure that is what gets run. Without the @latest included at the end of the package name, exec will prioritize speed and run an already-installed rails if it exists.