Install nix-direnv on wsl
Now that I've got a nix flake for my blog on WSL, I wanted to add direnv and nix-direnv to load the dev shell in my flake whenever I change into the directory where my blog lives. I've used this and similar setups in all the projects I've worked on for the past 5 years at least, and really like it. With it, you just change directory to where your code lives and your developer environment for that project gets loaded for you. Not only that, but after you've loaded the developer environment from your flake, it will cache and reuse it until it changes and needs reevaluating. That keeps the experience nice and snappy.
The way this works is by adding a .envrc file to a directory. If direnv
finds this file in a directory you've changed into, and it's got permission to
run it (more on this later), then it will run the script to setup your
environment. Not only can we use this to load our flake's developer shell, but
we can also export environment variables and run other setup scripts required
to work on our project.
In addition to having the environment load when you change directory, some
editors and IDEs are, or can be made, direnv aware. For example, I used to
use VS Code with an extension that loaded my direnv environment whenever the
project root had a .envrc file.
Now that I've given you plenty of reasons to use direnv, let's have a look at
how to set it up.
Installing direnv
At first I tried installing direnv using my user's nix profile, as I'm used
to managing my Linux tools with nix and thought it would be a chance to learn
a bit more about my tools. To do this I ran:
nix profile add nixpkgs#direnv
However, once I got to the install step
that adds a direnv call to ~/.bashrc I ran into a problem. When .bashrc
gets evaluated, my user's nix profile isn't yet loaded, so direnv is nowhere
to be found. Rather than play with evaluation order to try and make things work,
I just decided to install direnv using the Ubuntu tools.
nix profile remove direnv
sudo apt install direnv
This worked as expected, and direnv was ready to go.
nix-direnv
Out of the box, direnv has some basic nix flake support. However,
nix-direnv adds some things
that make our lives even better. Firstly, it stops nix from garbage collecting
(i.e. deleting) your shell environments until you explicitly choose to clean
them up. That means that your environment is cached and loads quickly after the
first run. Secondly, some other implementations like lorri require a daemon to
run on your system. nix-direnv does not.
A quick apt search nix-direnv shows that nix-direnv isn't available on
Ubuntu -- at least with its default package repos. However, we've got a
working nix installation, so let's just use that. Things have changed since
the last time I installed Nix packages globally without a NixOS configuration.
It looks like the new hotness with the nix command is to use nix profile.
Per the nix-direnv README
we run the following. I'm using add instead of install because install
doesn't appear in the help for my nix profile.
nix profile add nixpkgs#nix-direnv
Again, per the instructions, we then make nix-direnv's functionality available
to direnv by sourcing some stuff into direnv's rc file. Edit
$HOME/.config/direnv/direnvrc and add.
source $HOME/.nix-profile/share/nix-direnv/direnvrc
For completeness, the reason we're doing this system wide and not in our
project specific flake is that we need nix-direnv to help us load the flake,
so we can't get it from the flake environment.
Loading developer environments
Now we've got our tooling in place, we can tell it to load our developer shell
whenever we enter our project's directory. We do this by adding a .envrc file
in the directory where we want direnv to load things for us.
Given all we currently need is for direnv to load our developer shell, we just
need to add a .envrc file at our project root that contains the following.
use flake
That's it. It's that simple. Nix, direnv, and nix-direnv will take care of
the rest by finding our flake and putting its default developer shell into our
environment for us. No running nix develop every time we want to work on the
project. nix-direnv will also unload the environment when we leave the
directory.
A note on security
The one final thing we need to do before direnv will load our .envrc file
is to whitelist it. In the directory with the .envrc file, run the following.
direnv allow
We need to run this any time we have a new or modified .envrc file. If
direnv didn't require each user to whitelist the file on their machine,
then we'd possibly have arbitrary code from the internet running every time we
entered a project. For example, if you cloned a project to work on locally and
the project contained a .envrc, whatever shell commands were in that file
would run as soon as you entered the directory. Not good. So if you ever see
direnv asking you to allow a .envrc file that you didn't write, I strongly
encourage you to look inside and understand what it's doing before allowing it.