Simplify Version Management: Switching from NVM and Pyenv to Proto

Version Management Made Easy with Proto

Context

When you are working on just one project, you probably don't need to worry about installing different versions of languages and tools on your machine. But if you are working on multiple projects, some of them might require different versions, especially for languages that frequently update.

I mostly work with node.js and python. Some projects I handle require different versions of these technologies. This includes creating new projects and maintaining older ones. Switching between projects also means switching between different versions of the technologies.

With node.js, I have always used nvm to change the version of node when necessary. For python, I found that pyenv was helpful. However, it was sometimes problematic to install different tools and versions, and to remember to switch between them.

Automating this process is possible but requires a few extra steps. With nvm, we can create a script that runs with the cd command to check for an .nvmrc file or use a shell plugin. With the correct setup, pyenv will automatically choose the right python version.

So, we can easily manage those versions, right? Right. For each language, there is a tool to manage multiple versions installed on the system. We can find the right tool, learn how to use it, and apply it. But can we simplify and standardize this solution across different languages?

Proto

Lately, I discovered a tool called proto - "proto is a pluggable version manager, a unified toolchain." This is quote from the tool documentation that can be found here:

https://moonrepo.dev/proto

Basically, it is a tool to manage versions for different programming languages and other tools. It supports multiple languages, detects versions based on context, and verifies checksums to ensure the source is trusted. It is also cross-platform and allows for custom tooling through plugins.

Since it supports node.js, npm, pnpm, yarn, and python(this one is still as "experimental"), I decided to give it a try.

Installation

Before installation, there are a few requirements. You need to have git, unzip, gzip, and xz installed. These tools are used to fetch versions and work with archives. You can easily install them using system package managers like brew or apt.

To install proto, the authors provide a script:

curl -fsSL https://moonrepo.dev/install/proto.sh | bash

It will check if the requirements are met and then install proto.

That's it, now proto is installed. It will also automatically add the necessary paths to your shell profile, like this:

export PROTO_HOME="$HOME/.proto"
export PATH="$PROTO_HOME/shims:$PROTO_HOME/bin:$PATH"

Since everything will be stored in ~/.proto/, it will be easy to uninstall by deleting that directory and removing those exports from your profile.

Version detection

Now, when we go to our project, proto needs to detect which versions of tools we are using. It can find this information from the tool's ecosystem files, like .nvmrc or package.json. Alternatively, it can be specified in a .prototools file. The file structure is simple; it just contains tool = version.

An example .prototools file might look like this:

node = "22.1.0"
pnpm = "9.1.1"
python = "3.8"

There are also other ways to specify versions.

Command line:

proto run node 22.1.0

Environment variable:

PROTO_NODE_VERSION=22.1.0 proto run node

Or global versions that are stored at ~/.proto/.prototools.

If the version cannot be detected or found, it will show an error.

Version pin

The .prototools file can be modified manually, but you can also use the proto pin command to save versions to the file. For example:

proto pin node 19

Will save:

node = "~19"

Using flag --global will pin the version globally in ~/.proto/.prototools.

Auto Install and settings

By default, it will not automatically install all tools. You can do it manually using the command:

proto use

This will download and install all the tools and plugins specified in the .prototools file.

However, you can enable automatic installation by changing the settings in .prototools.

[settings]
auto-install = true
telemetry = false

Or with the environment variable PROTO_AUTO_INSTALL.

This way, if a required tool is specified but not available on the system, it will be installed automatically.

There are more settings available, such as detect-strategy, which lets you choose how to detect versions, and telemetry, which collects anonymous usage data (the default is true, but you can turn it off by setting it to false). Other settings are listed in the documentation.

Installing tools manually

To manually install, we can use the proto install command with the tool and version as arguments:

proto install python 3.11.9

But what versions are available to install? To check that, use the command proto list-remote with the tool as an argument:

proto list-remote python

This will list all available versions to install. To list all installed versions, use a similar command proto list with the tool as an argument.

This is problematic for python, which is supported (experimental). proto installs only pre-built versions and python doesn't support all of them. Building from source will be supported in the future, as stated on the plugin's GitHub page.

In the python plugin repository on GitHub, I found a list of available versions:

https://raw.githubusercontent.com/moonrepo/python-plugin/master/releases.json

To make it easier to use, if you have installed jq, you can use it:

curl https://raw.githubusercontent.com/moonrepo/python-plugin/master/releases.json | jq 'keys'

This command will list all the versions that can be downloaded.

Plugin installation with gojq example

If you don't have jq installed, you can use .proto to install gojq (a go implementation of jq).

proto plugin add gojq "source:https://raw.githubusercontent.com/stk0vrfl0w/proto-toml-plugins/main/plugins/gojq.toml"
--global

This will add the globally available plugin for gojq, and now you can install it:

proto install gojq --pin --global

This will install gojq globally and pin its version in ~/.proto/.prototools.

Now, you can use the previous command to list all python versions that can be installed with proto using gojq instead of jq.

curl https://raw.githubusercontent.com/moonrepo/python-plugin/master/releases.json | gojq 'keys'

Supported tools

Besides node.js, npm, pnpm, yarn, and experimental python, there are more supported tools. Built-in support is also available for bun, deno, go, and rust. Additionally, many more tools are available as third-party plugins (like the example of gojq above). You can find a complete list in the documentation here:

https://moonrepo.dev/docs/proto/tools

So, if I want to try another language like go or rust, I will use proto to install it.

Summary

Managing multiple versions of programming languages and tools can be challenging, especially when working on various projects that require different versions. While tools like nvm for Node.js and pyenv for Python help, they can be cumbersome to manage. Proto offers a unified solution for version management across multiple languages and tools, including Node.js, npm, pnpm, yarn, and experimental support for Python. It automates version detection and installation, supports custom tooling through plugins, and simplifies the process with a single configuration file. This guide covers the installation, configuration, and usage of Proto to streamline your development workflow.