# Simplify Version Management: Switching from NVM and Pyenv to 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](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:

```bash
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:

```bash
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:

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

There are also other ways to specify versions.

Command line:

```bash
proto run node 22.1.0
```

Environment variable:

```bash
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:

```bash
proto pin node 19
```

Will save:

```bash
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:

```bash
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`.

```bash
[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](https://moonrepo.dev/docs/proto/config#available-settings).

## Installing tools manually

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

```bash
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:

```bash
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](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:

```bash
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`).

```bash
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:

```bash
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`.

```bash
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](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.
