This post was written in 2015. Since then, I've continued to use
direnv and still rely on it heavily. In 2020, I wrote about how my setup has evolved into a more powerful setup - please check out my more practical direnv post!
direnv is a nifty little shell tool I came across recently which looks extremely useful for developers who work across multiple languages or projects.
direnv automatically update environment variables when you enter/exit particular directory trees. Much as you might have
PATH variables set globally in your
direnv allows you to control these on a per-directory basis.
This post explores a few simple examples for usage in Python, Go and Java; see below for further resources.
For Mac users, installation is easiest using Homebrew (
brew install direnv).
Automatic Python virtualenv switching #
For Python projects
virtualenv provides separation of dependencies, but more than once I've found myself forgetting to activate/deactivate when moving around between projects. Direnv again makes this easy and foolproof:
When you enter the folder subtree direnv will automatically pick up an existing
virtualenv environment and activate it.
$ mkdir a b $ echo "layout python" > a/.envrc $ echo "layout python" > b/.envrc $ cd a direnv: error .envrc is blocked. Run `direnv allow` to approve its content.
This warning occurs because direnv has detected a new or changed
.envrc file - a good security precaution.
$ direnv allow direnv: loading .envrc Running virtualenv with interpreter /usr/local/bin/python New python executable in /private/tmp/a/.direnv/python-2.7.10/bin/python2.7 Also creating executable in /private/tmp/a/.direnv/python-2.7.10/bin/python Installing setuptools, pip, wheel...done. direnv: export +VIRTUAL_ENV ~PATH $ pip install pycrypto Collecting pycrypto Installing collected packages: pycrypto Successfully installed pycrypto-2.6.1
We now have a virtualenv inside
a with pycrypto installed:
$ pip freeze pycrypto==2.6.1 wheel==0.24.0
Changing directory to
b is enough to automatically switch virtualenv.
$ cd ../b direnv: error .envrc is blocked. Run `direnv allow` to approve its content. $ direnv allow [SNIP] direnv: export +VIRTUAL_ENV ~PATH $ pip freeze wheel==0.24.0
Subsequent changes of directory won't prompt for
direnv allow, and allows seamless changes of virtualenv:
$ cd ../a direnv: loading .envrc direnv: export +VIRTUAL_ENV ~PATH $ pip freeze pycrypto==2.6.1 wheel==0.24.0
Automatic setting of GOPATH #
If you're developing in Go,
direnv can help automatically maintain a separate
GOPATH for each independent project. This can be useful where you're using different versions of dependencies across projects, and wish to maintain some separation - without remembering to change
GOPATH yourself every time you switch project.
Quite simply, place the following in
.envrc at the root of each Go project:
Direnv will do the rest automatically; setting
GOPATH and also updating
PATH to include
Different versions of a tool for different projects #
While I'm almost always working with Java 8, for legacy reasons I'm also working on a Java 7 codebase at the moment. There's nothing intrinsically wrong with using JDK 1.8 for JDK 1.7 development, but there are some risky areas I'd rather avoid. Additionally we have a need to use a fixed Gradle version rather than using the system one (or, curiously, the Gradle wrapper).
So, I set my general purpose JDK to 1.8 by placing this in my home
# On OS X, discover JAVA_HOME for a major JDK version by using the java_home utility export JAVA_HOME=$(/usr/libexec/java_home -v 1.8) # direnv helper to add this to PATH. export PATH=...:$PATH also works. PATH_add $JAVA_HOME/bin
And override the Java and Gradle version specifically for the legacy project by placing this in my
export JAVA_HOME=$(/usr/libexec/java_home -v 1.7) # PWD is always set to the directory in which this .envrc file was found # In this case, our legacy version of gradle is in ~/projects/something/legacy-gradle export GRADLE_HOME=$PWD/legacy-gradle # Update paths as previously PATH_add $JAVA_HOME/bin PATH_add $GRADLE_HOME/bin
Now, for working with Java anywhere inside my home folder, direnv ensures that I'm using Java 1.8 automatically. However, if I run Java, Gradle, or check the
GRADLE_HOME environment variables anywhere under
~/projects/something, it's going to be the correct, older version.
Note: this example could be refactored into a custom
usefunction, but I wanted to start with something that would be reasonably familiar looking to most *nix users.
Further reading #
- direnv.net is an OK starting point, but I felt didn't do enough to show the power of the tool.
- The direnv man page provides some other simple examples.
- The direnv standard library provides convenience wrappers for Python, Go, Node, Ruby, Perl and Nix, and is worth looking at.
- Rachid Belaid has done a more extensive writeup specifically around using direnv for Go development.