Michael Anhari

Manage programming runtime versions with FZF and asdf

Overhead view of tools

I used to use RVM and later on rbenv to manage Ruby versions on my machine. Both of which worked fine, but I needed to manage languages other than Ruby.

I needed node for JavaScript development, and both python2 and python3 to make the most of neovim. I wanted erlang and elixir to explore functional programming.

I started looking around for something similar to rbenv that worked for multiple languages. After a quick search on DuckDuckGo, I found asdf. The selling points on their README sounded like exactly what I had been looking for:

- single CLI for multiple languages
- consistent commands to manage all your languages
- single global config keeping defaults in one place
- single .tool-versions config file per project
- support for existing config files .node-version, .nvmrc, .ruby-version for easy migration
- automatically switches runtime versions as you traverse your directories
- simple plugin system to add support for your language of choice
- shell completion available for common shells (Bash, Zsh, Fish)

asdf works by installing plugins for the languages you need, which provide the mechanisms for installing versions of those languages. For example:

asdf plugin add ruby
asdf install ruby 3.0.0

asdf plugin add nodejs
asdf install nodejs 14.15.4

FZF

At the time of discovering asdf, I had already run into FZF (a command-line fuzzy find tool), and thought it would be cool to integrate the two.

The FZF repo comes with a bunch of examples, including two for asdf to install/remove language versions:

# Install one or more versions of the specified language
# e.g. `vmi rust` # => fzf multimode, tab to mark, enter to install
# if no plugin is supplied (e.g. `vmi<CR>`), fzf will list them for you
# Mnemonic [V]ersion [M]anager [I]nstall
vmi() {
  local lang=${1}

  if [[ ! $lang ]]; then
    lang=$(asdf plugin-list | fzf)
  fi

  if [[ $lang ]]; then
    local versions=$(asdf list-all $lang | tail -r | fzf -m)
    if [[ $versions ]]; then
      for version in $(echo $versions);
      do; asdf install $lang $version; done;
    fi
  fi
}
# Remove one or more versions of the specified language
# e.g. `vmi rust` # => fzf multimode, tab to mark, enter to remove
# if no plugin is supplied (e.g. `vmi<CR>`), fzf will list them for you
# Mnemonic [V]ersion [M]anager [C]lean
vmc() {
  local lang=${1}

  if [[ ! $lang ]]; then
    lang=$(asdf plugin-list | fzf)
  fi

  if [[ $lang ]]; then
    local versions=$(asdf list $lang | fzf -m)
    if [[ $versions ]]; then
      for version in $(echo $versions);
      do; asdf uninstall $lang $version; done;
    fi
  fi
}

With those two added as zsh functions and asdf/FZF installed, you can fuzzy-find your way into installing/removing language runtime versions using vmi or vmc on the command-line:

Using VMI command to install elixir 1.11.2

Newsletter

I'm working on sending out a weekly newsletter. I'll make it as easy as possible to unsubscribe at any time.