Nix
Nix is a functional programming language used for the Nix package manager. Instead of a declarative approach to installing packages, in Nix, everything is declarative. This greatly improves reproducability and reliability due to the fact that a system can almost fully be reproduced on other hardware.
Getting started
Nix can be installed on almost any linux distribution and even on MacOS. Although the best way to use Nix is via NixOS, a complete linux distribution, only using the Nix package manager. To install Nix, follow the instructions on the website.
After installing Nix, new shell environments can be created. These are
temporary shells, used to test packages or commands. To create a shell
environment run nix-shell with the -p (--packages) flag. This will
install the specified packages into the shell environment.
$ # cowsay is not available
$ cowsay
The program 'cowsay' is currently not installed.
$ nix-shell -p cowsay lolcat
[nix-shell:~]$ cowsay Hi | lolcat
____
< Hi >
----
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
[nix-shell:~]$ # Install python into current shell environment
[nix-shell:~]$ # This actually creates a nested shell inside of the
[nix-shell:~]$ # current shell environment.
[nix-shell:~]$ nix-shell -p python3
[nix-shell:~]$ python --version
Python 3.10.11
[nix-shell:~]$ # Exit the Nix shell
[nix-shell:~]$ exit
$ # cowsay is again not available
$ cowsay
The program 'cowsay' is currently not installed.
This example above is not reproducable yet. It installs the latest version of
a given pacakge, in this example cowsay, lolcat and python3. If a newer
version breaks anything, the commands above may not work as described. Here
comes the -I flag into place. With this flag, the location of a package can
be defined. This means, that a specific version can be pin-pointed to so this
version will always be used:
nix-shell -p git --run "git --version" --pure -I nixpkgs=https://github.com/NixOS/nixpkgs/tarball/2a601aafdc5605a5133a2ca506a34a3a73377247
git version 2.33.1
In the example above, we directly run a command with the --run flag, followed
by the command: git --version. With the --pure flag, we discard most
environment variables. This is to avoid using the (maybe) already installed
git version and rather use git from Nix.
Reproducable scripts
A common issue with bash scripts is, that one or more commands are only available on the machine, that the original command is being ran on. This means that a script that might work on another machine, doesn't work on yours because you might not have all the packages/commands used in the bash script. An example could be:
#! /bin/bash
curl https://github.com/NixOS/nixpkgs/releases.atom | xml2json | jq .
Here the script requires the following commands to be present on the machine:
curl, xml2json and jq. curl is often times installed, however xml2json
and jq are both a bit more exotic. This results in only one command being
executed and the other two just fail.
With Nix we can declare a shell environment with all the required packages installed. This means, that a script will run on every platform that also supports Nix.
With this now in mind, we can reproduce the command from earlier reliably. For
this task, a script needs to be defined, which uses the nix-shell as a
interpreter. After that, we specify a shell to be used, in our case bash
and we also want to remove most existing environment variables using --pure.
We then proceed to define what packages we want to use. In our case those are:
bash, cacert, curl, jq and python3Packages.xmljson.
Since we want 100% reproducability, Nix needs to know, from where to take our
specific pacakges from. This is to avoid different outputs for different
versions of an application. With the -I argument, we tell nix to use this
specific version of Nix's pkg repository.
Laslty we run our commands with the line curl ...:
#!/usr/bin/env nix-shell
#! nix-shell -i bash --pure
#! nix-shell -p bash cacert curl jq python3Packages.xmljson
#! nix-shell -I nixpkgs=https://github.com/NixOS/nixpkgs/archive/2a601aafdc5605a5133a2ca506a34a3a73377247.tar.gz
curl https://github.com/NixOS/nixpkgs/releases.atom | xml2json | jq .