If you are like me and have lots projects requiring different versions of global applications like terraform, python, node, etc then this solution is for you. Well, this solution is for you if you are on Mac or Linux at least.

Use Cases  

  • Upgrading to the latest application one project at a time.
  • Testing with different versions

Solution Overview

Dynamically update PATH environment variable for each project based on current directory or parent directory. For this I use direnv which is a great tool that extends your shell to load and unload environment variables depending on the current director.  And it probably supports you shell of choice as long as it is  bash, zsh, fish, tcsh, or Elvish

Tutorial Requirements

This how to will use Mac OS, homebrew, and terraform; however this solution will work for any platform or tool set.

  1. Mac OS X
  2. homebrew

Installing homebrew is easy.

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"

3. direnv

You can easily install direnv with homebrew. To install direnv on other systems see the direnv Installation Documentation

brew install direnv

4. Hook direnv into your shell

To hook direnv into your shell following the instructions https://direnv.net/docs/hook.html for you specific shell.

Files

You can find the files created in this tutoril in my tips-tricks-workaround git repo.

Tutorial

  1. Install both terraform 12 and 13 using homebrew.  Output of the commands has been ommitted:
brew install terraform       # installs current version, 13
brew install terraform@0.12

Let's take a look at what was installed and where:

# ls -od /usr/local/opt/terraform* /usr/local/bin/terraform* | awk '{print $8,$9,$10}'

/usr/local/bin/terraform -> ../Cellar/terraform/0.13.0_1/bin/terraform
/usr/local/opt/terraform -> ../Cellar/terraform/0.13.0_1
/usr/local/opt/terraform@0.12 -> ../Cellar/terraform@0.12/0.12.29
/usr/local/opt/terraform@0.13 -> ../Cellar/terraform/0.13.0_1

2. Since we don't want to accidentally run the wrong version of terraform we will remove /usr/local/bin/terraform with the brew unlink command:

# type terraform
terraform is /usr/local/bin/terraform

# brew unlink terraform
Unlinking /usr/local/Cellar/terraform/0.13.0_1... 1 symlinks removed

# type terraform
type: Could not find 'terraform'

Perfect, now we will not accidenitly run terraform 13 on a terraform 12 state file and update it.

3. I'm going to assume all your projects are under the same directory such as Projects.   Here is the Project layout for this tutorial.  

/Projects/project-acme/
/Projects/project-beta/
/Projects/project-charlie/

4. Now will setup direnv to dynamically update your PATH based on each project needs.  In your root project directory, Projects, add the following lines to the .envrc

export TF12_PATH=/usr/local/opt/terraform@0.12/bin
export TF13_PATH=/usr/local/opt/terraform/bin  # Terraform 13

5.  Create a .envrc file in each project and to add the needed version of terraform to the project.

source_up

PATH_add $TF13_PATH

The source_up is a direnv command that will walk up your directory tree to / looking for another .envrc and we need to do this first to load the variables set above.

The PATH_add is a direnv command to easily add to front of your PATH environment variable.   Can you also do the shell way of PATH=$TF13_PATH:$PATH

5. That is it.  Now when you cd to different directories you path will be updated to point to the correct version of terraform that you want. Let's see how that works.

~/Projects · (master±)
# type terraform
type: Could not find 'terraform'

~/Projects · (master±)
[1]# cd project-acme/

~/Projects/project-acme · (master±)
# type terraform
terraform is /usr/local/opt/terraform@0.13/bin/terraform

~/Projects/project-acme · (master±)
# cd ../project-beta/

~/Projects/project-beta · (master±)
# type terraform 
terraform is /usr/local/opt/terraform@0.12/bin/terraform

Alternate Configuration

Default value

Normally I do not recommend having a default value and instead specifing it for each project because it makes future upgrades easier.  However, if you want to have a default value you can update your root .envrc as below with a PATH_add  

export TF12_PATH=/usr/local/opt/terraform@0.12/bin
export TF13_PATH=/usr/local/opt/terraform@0.13/bin

# Optional: If you want a default terraform, add it here.
PATH_add $TF12_PATH

Use rc/profile

Instead of having a root level .envrc file you can put the TF12_PATH and TF13_PATH environment variables in your account rc/profile files.  


bash ~/.bashrc
zsh  ~/.zshrc  
fish  ~/.config/fish/config.fish