How to manage multiple CLI tool versions with ease: direnv + asdf on macOS
A helpful guide to setup direnv + asdf on macOS and bring order to versioning chaos
We all work on different projects with different tools and different versions. And usually, chaos arises with all sorts of incompatibility.
Let's now think about this scenario: You are working with a customer using Kubernetes 1.20, managed via a Terraform 1.0.7 project. Then, a new customer arrives, and the status of their infrastructure is Kubernetes 1.13 and Terraform 0.14.11.
A way to manage multiple versions of CLI tools is mandatory. There are a lot of tools specialized in one single CLI, for example, terraenv, tfenv, kbenv, etc, but all of them use a different logic, each with a slightly different CLI interface. We need something centralized to manage everything.
The solution
Here comes the cavalry: asdf. asdf is a tool to manage other tool versions using a plugin architecture. We will also use direnv, an extension for the shell. It augments existing shells with a new feature that loads and unloads environment variables depending on the current directory.
This combo will automatically set environment variables and change tool versions when entering and exiting the project/directory that requires them!
Let's install everything
First of all, make sure to uninstall direnv and asdf via brew - we will install everything with the git process.
brew remove asdf
brew remove direnv
We will follow the guide on this page to install asdf: http://asdf-vm.com/guide/getting-started.html#_2-download-asdf
At the time of writing, the last asdf version is v.0.8.1.
Let's clone the asdf source code:
git clone https://github.com/asdf-vm/asdf.git ~/.asdf --branch v0.8.1
This command will clone the project in our home directory under the .asdf
folder.
Now add the following in your .bashrc
or .zshrc
file:
. $HOME/.asdf/asdf.sh
Reload your CLI and now you should have the asdf command available.
Let's proceed with the installation of direnv.
asdf plugin add direnv
Now that the plugin is available, we also need to use asdf to install direnv like this:
asdf install direnv 2.28.0
asdf global direnv 2.28.0
Let's go ahead and install some other plugins:
asdf plugin add terraform
asdf plugin add kustomize
asdf plugin add helmfile
asdf plugin add kubectl
asdf plugin add helm
Now edit your .bashrc
or .zshrc
file and add the required hook:
eval "$(asdf exec direnv hook bash)"
Replace bash with zsh if needed.
The last thing to do is to run the following command:
asdf direnv setup --shell zsh --version 2.28.0
Reload your CLI, and now everything should be ready to be configured.
Home setup
Create two files in your home root:
cat <<EOF> .envrc
use asdf
EOF
cat <<EOF> .tool-versions
direnv 2.28.0
terraform 1.0.7
kustomize 3.5.3
kubectl 1.20.2
EOF
The first line with the direnv version is mandatory, otherwise the integration will not work.
Now execute:
asdf install
asdf exec direnv allow
The first command asdf install
will read the .tool-versions
file and install all the versions of the programs defined, if the asdf plugin is available.
The second command asdf exec direnv allow
will execute direnv to allow the execution of what we have defined in the .envrc
to run.
That's it, you should be able to use all the tools defined in the .tool-versions file with the correct versions!
Setup a new project/folder
Now we have configured a global version of tools, in our shell. How do we configure on a per-project basis? In the same way we configured the home, for example if we have a project that needs terraform 0.14.11 and some environment variables exported , we can create the two files, .envrc and .tool-versions, with the following content:
cat <<EOF> .enrvc
use asdf
export AWS_REGION=eu-west-1
EOF
cat <<EOF> .tool-versions
terraform 0.14.11
EOF
Activate the environment with: asdf exec direnv allow
and install missing tool versions with asdf install
.
Conclusions
I hope this article has been helpful, because we have been looking for a centralized way to manage different tool versions for a long time. Thanks to Luca Novara - he was the first to find this solution that the whole SIGHUP team now uses.