Declarative and reproducible developer environments
Contents
Declarative and reproducible developer environments¶
In the Ad hoc developer environments tutorial we looked at providing shell environments for when we need a quick’n’dirty way of getting hold of some tools.
In this tutorial we’ll take a look how to create reproducible shell environments given a declarative configuration file called a Nix expression.
When are declarative shell environments useful?¶
This is the quickest approach to getting started with Nix:
use single command to invoke it via
nix-shellit works across different operating systems (Linux / MacOS)
you share the exact same environment with all developers
Developer environments allow you to:
provide CLI tools, such as
psql,jq,tmux, etcprovide developer libraries, such as
zlib,openssl, etcset shell environment variables
execute bash during environment activation
Getting started¶
At the top-level of your project create shell.nix with the following contents:
{ pkgs ? import (fetchTarball "https://github.com/NixOS/nixpkgs/archive/06278c77b5d162e62df170fec307e83f1812d94b.tar.gz") {} }:
pkgs.mkShell {
buildInputs = [
pkgs.which
pkgs.htop
pkgs.zlib
];
}
Note
To understand the first line, read through pinning nixpkgs tutorial.
We import nixpkgs and make a shell with which and htop available in $PATH.
zlib provides libraries and headers in case we’re compiling something against it.
To enter the environment:
$ nix-shell
these paths will be fetched (0.07 MiB download, 0.20 MiB unpacked):
/nix/store/072a6x7rwv5f8wr6f5s1rq8nnm767cfp-htop-2.2.0
copying path '/nix/store/072a6x7rwv5f8wr6f5s1rq8nnm767cfp-htop-2.2.0' from 'https://cache.nixos.org'...
[nix-shell:~]$
The command will start downloading the missing packages from the https://cache.nixos.org binary cache.
Once it’s done, you are dropped into a new
shell. This shell provides the packages specified in shell.nix.
Run htop to confirm that it is present. Quit the program by hitting
q.
Now, try which htop to check where the htop command is on disk.
You should see something similar to this:
[nix-shell:~]$ which htop
/nix/store/y3w2i8kfdbfj9rx287ad52rahjpgv423-htop-2.2.0/bin/htop
Customizing your developer environment¶
Given the following shell.nix:
{ pkgs ? import (fetchTarball "https://github.com/NixOS/nixpkgs/archive/06278c77b5d162e62df170fec307e83f1812d94b.tar.gz") {} }:
pkgs.mkShell {
buildInputs = [
pkgs.which
pkgs.htop
pkgs.zlib
];
shellHook = ''
echo hello
'';
MY_ENVIRONMENT_VARIABLE = "world";
}
Running nix-shell we observe:
$ nix-shell
hello
[nix-shell:~]$ echo $MY_ENVIRONMENT_VARIABLE
world
The
shellHooksection allows you to execute bash while entering the shell environment.Any attributes passed to
mkShellfunction are available once the shell environment is active.
direnv: Automatically activating the environment on directory change¶
Besides activating the environment for each project, every time you change
shell.nix you need to re-enter the shell.
You can use direnv to automate this process for you, with the downside that each developer needs
to install it globally.
Setting up direnv¶
At the top-level of your project run:
$ echo "use nix" > .envrc && direnv allow
The next time your launch your terminal and enter the top-level of your project direnv will check for changes.
$ cd myproject
direnv: loading myproject/.envrc
direnv: using nix
hello
Next steps¶
Towards reproducibility: pinning Nixpkgs to see different ways to import nixpkgs
To quickly set up a Nix project read through Getting started Nix template.