Why have project specific packages?
There are a few reasons why you would want to have project specific packages.
- Different package versions per project e.g. you are maintaining a project that uses an old version of Node.js (like this blog, I’m lazy), but use a newer version globally on your system.
- You don’t want to pollute your global environment with packages you’ll only use in an isolated project e.g. nixfmt to format *.nix files in my dotfiles.
- Easy on-boarding of a new team member e.g. packages are declared in the same project and it is easy to install and start contributing (more details coming below).
In the Nix ecosystem, this can be achieved using nix-shell.
Using nix shell
The command description reads.
“start an interactive shell based on a Nix expression”
Essentially, the command nix-shell
will build the dependencies specified in a nix derivation, but NOT the derivation itself.
It will then start an interactive shell which will have all the packages specified in the derivation available on your path.
Let’s say you want Python 2.7 (why though?). You can try this out by running:
nix-shell -p python
Nix will then drop you into an interactive shell with Python 2.7 available to you.
You can also specify your packages in a file. By default, nix-shell
will look for a file shell.nix
(or default.nix
if it can’t find that).
An example nix.shell
might look something like this.
let pkgs = import <nixpkgs> { };
in pkgs.mkShell rec {
name = "dotfiles";
buildInputs = with pkgs; [ nixfmt python39 python39Packages.typer ];
}
I use this to install packages only in my dotfiles directory.
- nixfmt to format *.nix files
- Python and Typer for nice CLI to handle re-building and rolling back my dotfiles (I’ll write a dedicated blog post describing this setup)
To drop into an interactive shell, just run nix-shell
without any arguments. Nix will now use nix.shell
to install and put packages onto your path.
Pretty nifty!
Automating with direnv
Wouldn’t it be cool if your interactive nix-shell could be activated every time you cd
into your project directory (and deactivated when you cd
out)?
Well, with direnv you can.
You can install direnv with Nix.
nix-env -iA direnv
Then in each project you’d like to automate, create a .envrc
file with the following:
use_nix
Allow direnv to automatically “watch” this directory (and sub-directories) by running direnv allow
.
Now, every time you cd
into your project directory, nix-shell
will automatically be run and you’ll be dropped into an interactive shell.
Cool!
Conclusion
Nix and direnv provide a very convenient method of keeping your project specific packages away from your system packages as well as providing enough autonomous convenience to “get out of your way”. I use this methodology in a bunch of projects and it works really well.
It’s worth mentioning devshell, a project built specifically around this type of tooling. It provides a framework like experience describing this setup.
Till next time.