Willy's blog

Nix journey part 2: replacing apt and brew

Even if I still cannot do much with nix, it still provide more advantages over other package managers:

  • Multi-platform (mac, linux, etc.)
  • Supports side-by-side installation of multiple versions of a package
  • Makes it trivial to share development and build environments
  • Nix ensures that installing or upgrading one package cannot break other packages
  • It has the biggest database of packages (over 80.000 packages)
  • I can run other people's commands, for example if I clone a repo and it says "run this nix command to have a development environment", then it doesn't matter if I don't know, I have already started using it.

A common situation I often have between linux and mac is running grip or sed. On linux, they are called GNU grep or GNU sed, and they are not the same as in mac (freebsd versin), they may not accept the same parameters. We are gonna see how can to avoid this using nix. And even without using complicated features, it can make your CI system more reproducible.

Remember nix is 3 things at the same time: an OS, a package manager and a language.

This post is about the package manager. I don't have much interest in the language, but more and more I think I'll have to learn it.

Let's start by acknowledging a source of confusion:

There is an old interface with counterintuitive commands (nix-env -iA ..., nix-shell -p ...), which I found hard to remember, and I don't get why they "commands" start with a dash (-). I'm used to cli's doing cli <command> [--options]. Nowadays there's a new cli called just nix, let's see if we can do everything with it.

And make sure you have installed Nix: the package manager in your system. The installation is straightforward. I was personally blocked, because at some point in my dotfiles I was hardcoding the PATH, making nix never appear 🤦‍♂️.

And enable flakes.

Installing packages

Install a package like on brew or apt.

nix profile install nixpkgs#htop

See also the profile install command reference.

In the old version of nix, we would run:

nix-env -iA nixpkgs.htop

Nix forces us to specify a "repository" (or "namespace") when installing a package (nixpkgs), which could be different, like github. And I think this is a good thing. From my understanding, nix doesn't care where the package is, because each package has a lock file, tracking all the dependencies. Okay, it could be a problem if one of the "repositories" is down, but using nixpkgs mainly and github for niche packages should be fine.

What are profiles?

Disclaimer: I may be wrong on this, I'm starting to understand it.

The way to see profiles is like "your user's packages". nix profile links packages to your ~/.nix-profile/. You can specify other's profiles by using the flag -p.

You can find more in the package-management section of the manual and the profile command reference

Searching packages

nix search nixpkgs#htop

Check the search command reference

We see again nixpkgs, because we have to let nix know from where, and then what we are looking for (htop in this case). To remember the word, I split it like this: nix-p-k-gs.

For example, I've made a flake package, hosted on github, and you can search what is offering, by specifying the "repository" only (no #):

nix search 'github:woile/wpa_passphrase_rs'

You can also search packages on nix search index, but the commands shown are for the old nix interface, using nix-env or nix-shell.

Also, you can provide a regex like firefox|chrome, run nix search --help for more examples.

Removing packages

Now this is a bit tricky, to remove you cannot type htop, you have to specify which dependency you want to clean. I think this is because one of your packages may depend on the version of another package, and if you also installed another version of the same package, then if you remove both, the original program that depends on one of them may break.

The solution to this is to list the installed packages in your profile, and then remove by position of said program.

nix profile list
nix profile remove 4

Check the profile remove command reference.

Open a package on a shell

This can be useful to test in isolation without installing a package in your profile.

nix shell nixpkgs#htop nixpkgs#gnused nixpkgs#youtube-dl

sed --help
htop --help
youtube-dl --version
CTRL+D # exit

Nix package manager (npt)

Because I'm not used to most of the seen commands, I built a thin abstraction on top called npt. Which aims to be a humble succesor to apt. It also requires less characters to use it.

The installation, as we've seen before, can be done by running:

nix profile install 'github:woile/npt#npt'

And then run npt --help and check the commands, I hope it helps the transition to nix.

Now you can install packages by doing:

npt install htop github:woile/wpa_passphrase_rs#wpa_passphrase
# or npt i

It's still a work in progress, but a good start. I want to add to it the ability to show the executed nix commands, as a way to learn.

Reproducible scripts

Remember when I said even if you don't know much, someone else might, and having nix helps for this? and remember when I talked about my problems with sed and grep?

Turns out nix can help in both of these situations, someone can write a reproducible shell script which you would execute, even without knowing much, it would work.

A minor problem is that we cannot use flakes yet, meaning we cannot run nix shell and instead, we have to rely on nix-shell. But it's coming, see #5189, #4715.

In the meantime, let's try to solve the issue with what we have.

touch gnu-example.sh
chmod +x gnu-example.sh
vim gnu-example.sh

And paste the content of this script:

#! /usr/bin/env nix-shell
#! nix-shell gnused gnugrep

grep -V
sed --version

If run ./gnu-example.sh, it would work both on linux, mac and probably also on freebsd.

Take a look at this other example, you can install a specific version of python and even dependencies.

#! /usr/bin/env nix-shell
#! nix-shell --pure -i python -p "python38.withPackages (ps: [ ps.django ])"

import django
print(django.__version__)

This opens the door to replace pyenv and any virtualenv you will ever need.

Imagine creating a nix file specific to your project with its dependencies, that you load only when in the project folder. Say goodbye any version manager (pyenv, nvm, etc).

You can read more about building a shell.nix in the nix.dev tutorial.

I hope you've learned something with this post, and if you liked it, please let me know in the comments section below or tag me on hachyderm @woile.