A guide to manage your environment variables in a better way using direnv
Unclutter your .bashrc and provide a per-project scoping to your environment variables.
In this post, I’m going to discuss about a tool which I just found a few days ago called direnv
.
direnv
acts as an extension to your shell and it provides a feature using which you can load and unload environment variables depending on your current project directory.
This allows you to manage project-specific environment variables and not clutter your ~/.profile
, ~/.bashrc
or ~/.zshrc
files.
How direnv provides a per-project scoping?
To do that, it checks for the existence of an .envrc
file in the current and parent directories.
If it exists, the contents of .envrc
are loaded into your shell environment.
Features of direnv
direnv
com es with a bunch of features:
- It is fast enough to be unnoticeable on each prompt.
- Language-agnostic, so you can use it to build a solution with any programming language.
- Supports a variety of shells, the list comprises of
bash
,zsh
,fish
,tcsh
,elvish
. - Provides a set of utility functions
- Ability to create your own custom utility functions.
- No npm/other package dependency is required unlike
dotenv
. - Hooks directly into the shell — unlike
dotenv
which hooks into the process,direnv
hooks into the shell. Thus, it is available to any process within a given shell session.
This section was all about an intro to direnv
, how it works and it’s benefits.
In the next section, let’s talk about how we can install and set it up.
Installation
There are two steps to the installation of the direnv
:
- Installing the system package.
- Hooking into the shell.
Installing the direnv
system package
Hook into the shell
Now just restart your terminal or source your config file by calling source ~/.bashrc
or source ~/.zshrc
, whichever applicable.
That’s it for the setup and installation.
You can now use direnv
in any project you like.
We are going to discuss the usage instructions in the next section.
Note: As you might have noticed there are no setup instructions for Windows.
The reason for this beingdirenv
doesn’t natively support Powershell/CMD on Windows.
But you can still usedirenv
on Windows using Windows Subsystem for Linux (WSL).
I’ve written a blog post earlier aboutwsl
, so you can get to know how to get started withwsl
by checking out this blog post and then follow the instructions for Linux as discussed above.
Using direnv in your projects
To start using direnv
, you just need an .envrc
file in your project.
Let’s create a project folder named my-project
.
mkdir my-project
Create an .envrc
file in this folder with an environment variable called MY_NAME
:
echo export MY_NAME=shivam > my-project/.envrc
Now, move into your project directory by calling:
cd my-project
You’ll see the following error:
direnv
, by default, blocks itself to load the contents of .envrc
file into your session as a security precaution when you’re creating .envrc
for the first time or whenever you’ve modified the contents of .envrc
file.
This security precaution protects you in cases when you clone a project or unzip a project archive, you’re at a risk of wiping up your entire hard drive if you cd
into this project without checking the contents of .envrc
.
You must trust the contents of .envrc
of the project and to tell direnv
that you trust those contents and to load the contents, you must run direnv allow
.
Since this is our own project and we know what’s inside the .envrc
, we can easily provide trust to the content by executing the command:
direnv allow
direnv
will now load the contents into the shell, as you can see below:
To test out if it has loaded our environment variable MY_NAME
that we just provided earlier, print it’s value by using:
echo $MY_NAME
You must see the output as:
Now, once you move out of this project directory, you’ll see that direnv
automatically unloads the contents of .envrc
and you won’t be able to access the environment variable outside of your project.
Note: The contents of .envrc
are accessible even inside the subdirectories of your project. So, you can always access them if you’re inside anywhere in your project directory.
In this way, you can scope your environment variables to your project as
direnv
loads the environment variables only when you move inside the project directory and automatically unloads them when you move out.
This section was all about the usage and demo of how direnv
works within your shell.
In the next section, I’m gonna discuss how we can modify the contents of .envrc
file and how these changes reflect inside your shell environment.
Editing the contents of .envrc
In every project lifecycle, we need to provide additional environment variables or modify them as our project grows. In order to do that, you need to edit the contents of .envrc
.
You can edit the contents of .envrc
using any of your favorite text editors.
But keep in mind that, whenever you modify the contents, you need to allow direnv
to trust the contents of .envrc
using direnv allow
.
However, you can also avoid this behavior and load the contents in realtime whenever you edit the contents of .envrc
.
To achieve this, direnv
provides a handy command:
direnv edit
This command opens up the default text editor of your OS and once you modify the contents, save and close the file, all those contents will be loaded automatically without the need to run direnv allow
again.
You can even hook your favorite text editor with direnv edit
, so it will open up the provided editor instead of the os default.
To hook your favorite text editor, you need to provide a value for the $EDITOR
variable in your shell environment.
Hooking up VSCode with direnv:
So, if I want to hook direnv edit
with VSCode, all I need to do is to add the following line to my ~/.bashrc
or ~/.zshrc
:
export EDITOR="code --wait"
The contents of my ~/.bashrc
must now look like this:
You can now add or edit any variable by calling direnv edit
and it will automatically load into my environment.
In the next section, I’m gonna discuss about how you can setup a global .gitignore
for all of your .envrc
files inside all your projects.
Setting up a global .gitignore
Since .envrc
might contain sensitive information, it’s best to put this file under the .gitignore
in every project.
However, rather than adding it to each project’s .gitignore
file, you can setup a global gitignore file using the following command:
Now, you can add a single entry for .envrc
in this global .gitignore_global
file that we just configured and it will be ignored in all of your git repositories.
The contents of ~/.gitignore_global
file can contain:
Now if you remember about the features of direnv
that we discussed in the beginning of this post, I mentioned that direnv
also provides a set of utility functions.
So in the last section of this post, let’s discuss about what are those utility functions.
direnv Utility Functions
direnv
also provides you with a set of utility functions that are available in the context of .envrc
file.
As an example, the PATH_add
function can be used to expand the values of the $PATH
variable.
So instead of adding a new path using export PATH=<my-new-path>:$PATH
, you can write PATH_add my-new-path
and it will be automatically added to your $PATH
variable.
In the following example, I just added our project directory which we created earlier to the path by calling direnv edit
and entering the following into the .envrc
file:
And we can now test out the result if our current directory has been added to the $PATH
variable:
Other Utility Functions:
direnv
calls these utility functions as stdlib
functions. There are other useful functions available such as:
source_env
- this function loads up another.envrc
by specifying a path or its filename.path_add
- works just likePATH_add
but can be used to provide a path to another variable such as specifying theJAVA_HOME
for a directory.dot_env
- loads up a.env
file into the current environment without the need of installing any dependency. So, your project has less load of a directory.layout node
- adds$PWD/node_modules/.bin
to the PATH environment variable. So you can directly call executable commands present inside yournode_modules
without the need to refer the path such as calling./node_modules/bin/lerna bootstrap
. You'll be able to directly calllerna bootstrap
without the need to prepend the path.use node <version>
- loads the specified NodeJS version from a.node-version
or.nvmrc
file.watch_file <path> [<path> ...]
- adds one or more files to a watch list.direnv
will reload your shell environment on the next prompt, if any of the provided file changes.
You can find a list of all supported stdlib
functions at this documentation link.
Note: It is also possible to create your own extensions by creating a bash file at
~/.config/direnv/direnvrc
or~/.config/direnv/lib/*.sh
. These files are loaded before your.envrc
and thus allow you to make your own customized extensions todirenv
.
This was all about how you can manage your environment variables in a better way and scope them individually to your projects.
Hope you liked it and it was helpful. 😀