Some checks are pending
Gitea Actions Demo / Explore-Gitea-Actions (push) Waiting to run
400 lines
12 KiB
Markdown
400 lines
12 KiB
Markdown
---
|
|
title: varl's nixtapes vol.1
|
|
date: 2023-08-08
|
|
pub:
|
|
year: 2023
|
|
collection: nixtapes
|
|
---
|
|
|
|
> #### TL;DR
|
|
>
|
|
> At the end of this post we will have reached a point where we can load a
|
|
> shell with specific tools, and when we leave that shell, the global
|
|
> environment is untouched.
|
|
|
|
# volume 1: Setting up Nix
|
|
|
|
## Background
|
|
|
|
Over the last year or so I have experimented with using `nix` to manage
|
|
my development environments across multiple machines.
|
|
|
|
I like to keep e.g. my personal projects and work projects separated as
|
|
much as possible, so on my work projects I don't want my personal
|
|
development stack (tools, versions, etc.) to be loaded and when I work
|
|
on my personal projects the same applies.
|
|
|
|
I also want the development environments to be declarative, but the
|
|
actual use case is that I want them to be portable to other machines, so
|
|
I can spin up a new development environment fairly quickly.
|
|
|
|
There are other ways to achieve full isolation, e.g. a complete virtual
|
|
machine, but I don't like the overhead that brings.
|
|
|
|
I also _like_ the way I have my global environment setup, and prefer to
|
|
use that as a base, and then bring whatever tools I need for the context
|
|
I am working in into it.
|
|
|
|
I'm not sure when it happened, but `nix` the package manager (not "nix the
|
|
language", and not "nix the operating system"), is quite mature[^1]
|
|
across both Linux and MacOS, so I decided to brush off my "nix the
|
|
language" concerns and dive into it. Again.
|
|
|
|
[^1]:
|
|
Well, `nix-env` and other `nix-*` commands _are_ mature, but the
|
|
next-gen all-in-one `nix` CLI is definitely _not_ mature. It's not
|
|
even final:
|
|
https://nixos.org/manual/nix/stable/command-ref/experimental-commands
|
|
|
|
## Prerequisites
|
|
|
|
- A supported OS:
|
|
- Linux
|
|
- MacOS
|
|
- Windows with WSL2 with `systemd` enabled[^2]
|
|
- Comfortable using a shell
|
|
- A high tolerance for obtuse languages[^3]
|
|
|
|
[^2]:
|
|
Configuration guide:
|
|
https://devblogs.microsoft.com/commandline/systemd-support-is-now-available-in-wsl/
|
|
|
|
[^3]:
|
|
We will be using a bare minimum of "nix the language" to
|
|
accomplish our goals, so there are better ways to do most things I
|
|
do, but I don't understand any of them so I'm sticking to what I can
|
|
only describe as "simple but dumb".
|
|
|
|
## A note on "Nixes"
|
|
|
|
Nix is somewhat of an overloaded term, as it can mean:
|
|
|
|
- Nix the language
|
|
- Nix the CLI
|
|
- Nix the OS[^4]
|
|
- Nix the package manager
|
|
- Nix the utilities
|
|
- Nix the store
|
|
|
|
[^4]:
|
|
Most often called NixOS but searching for "nix" often winds up in
|
|
NixOS-land which is not always helpful.
|
|
|
|
I will use a subset of those in this guide, and do my best to
|
|
differeniate them.
|
|
|
|
The in-scope Nix terms will be:
|
|
|
|
- Nix the language (try as I might, there is no avoiding it)
|
|
- Nix the package manager
|
|
- Nix the utilities
|
|
|
|
This will give us all we need to accomplish our goal.
|
|
|
|
## A note on Nix installers
|
|
|
|
There are many ways to install Nix (the package manager) without NixOS,
|
|
and it works on any Linux distribution alongside the system package
|
|
manager.
|
|
|
|
It also works on MacOS, but there are a few rough patches of terrain. We
|
|
can mostly avoid them, or implement workarounds for the nasty ones.
|
|
|
|
### Official installer
|
|
|
|
> https://nixos.org/download
|
|
|
|
The official installer is available for Linux, MacOS, and Windows and is
|
|
the most unopinionated of the lot. It provides a predictable
|
|
environment, with opt-in to experimental functionality (that we will
|
|
_not_ use in this article anyway), and is managed by the Nix developers.
|
|
|
|
### Distro installer
|
|
|
|
> Arch Linux, Gentoo, Debian, Alpine, etc. all provide packages for
|
|
> their distro that can be used with various caveats.
|
|
|
|
I use the Arch Linux `nix` package on my workstation, for example, and
|
|
haven't had issues with it. It may be slightly out-of-date versus the
|
|
official one, but the package maintainer has been fast with updates so I
|
|
have never had issues.
|
|
|
|
I would be more sceptical of the Debian stable package version, and
|
|
perhaps opt for the official installer on that distro. Your mileage may
|
|
vary, but you are on Linux so you should be used to research and inform
|
|
your own choices.
|
|
|
|
### Determinate Nix installer
|
|
|
|
> https://github.com/DeterminateSystems/nix-installer
|
|
|
|
This installer is developed by [Determinate
|
|
Systems](https://determinate.systems/)[^5] and is an opinionated
|
|
installer, in that it makes different choices on how Nix should be
|
|
installed and setup than the official installer does. It leans into the
|
|
advanced functionality (often experimental) that the official installer
|
|
leaves off by default, and goes as far as to disable or dissuade users
|
|
from using the stable nix utilities.
|
|
|
|
[^5]:
|
|
A consultancy that provides services related to Nix training for
|
|
teams and companies.
|
|
|
|
It's a great installer, but if used, some configuration options must be
|
|
manually reverted to the official defaults. I tried this installer, but
|
|
wound up uninstalling and using the official one to avoid diving into
|
|
all the changes and assumptions they make.
|
|
|
|
## Installing using the official installer
|
|
|
|
If you decided to install Nix using the distro package manager or the
|
|
determinate systems' installer, feel free to jump ahead.
|
|
|
|
### Linux
|
|
|
|
Given you have Linux running `systemd`, with SELinux disabled, and can
|
|
authenticate with `sudo`, the multi-user installation method is
|
|
recommended:
|
|
|
|
```
|
|
sh <(curl -L https://nixos.org/nix/install) --daemon
|
|
```
|
|
|
|
If you cannot use the multi-user installation, you must use the
|
|
single-user installation method:
|
|
|
|
```
|
|
sh <(curl -L https://nixos.org/nix/install) --no-daemon
|
|
```
|
|
|
|
Follow along the installation, answering the prompts, and you will end
|
|
up with a Nix installation.
|
|
|
|
### MacOS
|
|
|
|
For MacOS a multi-user installation is recommended:
|
|
|
|
```
|
|
sh <(curl -L https://nixos.org/nix/install)
|
|
```
|
|
|
|
Same deal here, follow along the instructions and the result should be a
|
|
working Nix installation.
|
|
|
|
## Using the environment
|
|
|
|
Now that we have Nix setup, we can try out a few tricks. If you haven't,
|
|
you will need to open a new shell.
|
|
|
|
### Starting a shell with specific tools
|
|
|
|
> `nix-shell` reference manual:
|
|
> https://nixos.org/manual/nix/stable/command-ref/nix-shell
|
|
|
|
`nix-shell` starts an interactive shell based on a Nix expression, but
|
|
we will stick to our guns and define packages instead of an expression.
|
|
|
|
We can start a pure environment that inherits nothing from the global
|
|
environment:
|
|
|
|
```
|
|
nix-shell --pure
|
|
```
|
|
|
|
In which we don't even have `ping` defined:
|
|
|
|
```
|
|
[nix-shell:~/dev]$ ping
|
|
bash: ping: command not found
|
|
```
|
|
|
|
Type `exit` to go back to the global shell, and try this instead:
|
|
|
|
```
|
|
nix-shell --pure --packages cowsay
|
|
```
|
|
|
|
```
|
|
[nix-shell:~/dev]$ cowsay "hi nixer !"
|
|
____________
|
|
< hi nixer ! >
|
|
------------
|
|
\ ^__^
|
|
\ (oo)\_______
|
|
(__)\ )\/\
|
|
||----w |
|
|
|| ||
|
|
```
|
|
|
|
Hit `Ctrl-D` or type `exit` again. Multiple packages can be listed on
|
|
the commandline separated by spaces:
|
|
|
|
```
|
|
nix-shell --pure --packages less vim
|
|
```
|
|
|
|
Notice that when we request packages we don't have, we get these
|
|
messages:
|
|
|
|
```
|
|
these 2 paths will be fetched (8.06 MiB download, 38.84 MiB unpacked):
|
|
/nix/store/h5m8kaai6x64j1q6r7ffvq20f06r77m3-less-633
|
|
/nix/store/6j38m8vm8gp9a8qpw3b7dj9g50x1w95n-vim-9.0.1562
|
|
copying path '/nix/store/h5m8kaai6x64j1q6r7ffvq20f06r77m3-less-633' from 'https://cache.nixos.org'...
|
|
copying path '/nix/store/6j38m8vm8gp9a8qpw3b7dj9g50x1w95n-vim-9.0.1562' from 'https://cache.nixos.org'...
|
|
```
|
|
|
|
The short version is that each package and version is hashed and stored
|
|
in the Nix store (`/nix`). The consequence of this is that multiple
|
|
versions of the same package can exist at the same time (at the cost of
|
|
disk space) without causing conflicts across the environment. There is
|
|
also a long version[^6]. Probably a longer version somewhere as well.
|
|
|
|
[^6]:
|
|
Understanding the Nix store by looking at how Nix works:
|
|
https://nixos.org/guides/how-nix-works.html
|
|
|
|
### Installing packages into the environment
|
|
|
|
> `nix-shell` reference manual:
|
|
> https://nixos.org/manual/nix/stable/command-ref/nix-env#name
|
|
|
|
Being able to run a shell with specific packages is handy, and
|
|
`nix-shell` will be a underlying driver further into the nixtapes.
|
|
Instead let's take a look at the `nix-env` command.
|
|
|
|
Using `nix-env` we can manipulate a Nix user environment, which means
|
|
un/installing packages, searching for packages, and, rolling back the
|
|
environment.
|
|
|
|
Let's find some packages to install.
|
|
|
|
```
|
|
nix-env --query --available vim
|
|
|
|
vim-9.0.1562
|
|
```
|
|
|
|
Great, love that version. Let's install it:
|
|
|
|
```
|
|
nix-env --install vim-9.0.1562
|
|
|
|
installing 'vim-9.0.1562'
|
|
building '/nix/store/08xr544knfcahpn9xykql2xzsg374pxl-user-environment.drv'...
|
|
|
|
which vim
|
|
|
|
/home/varl/.nix-profile/bin/vim
|
|
|
|
readlink /home/varl/.nix-profile/bin/vim
|
|
|
|
/nix/store/6j38m8vm8gp9a8qpw3b7dj9g50x1w95n-vim-9.0.1562/bin/vim
|
|
```
|
|
|
|
Given that `/home/varl/.nix-profile/bin` is added to my `PATH` before
|
|
any paths that has my system version of `vim`, it will be picked up
|
|
correctly and when I run `vim` I will get the nix-installed version. No
|
|
shell-trickery and `vim` will be available across any other interactive
|
|
shells I open and use.
|
|
|
|
It is also possible to preserve installed alternative derivations of a
|
|
package, but I haven't had to use it much.
|
|
|
|
One thing we will use a lot though is the `--remove-all` flag. This
|
|
removes _all_ the installed packages in an environment, and only
|
|
installs the target package in the environment, so if we run:
|
|
|
|
```
|
|
nix-env --install vim nodejs python3
|
|
|
|
installing 'vim-9.0.1562'
|
|
installing 'nodejs-18.16.1'
|
|
installing 'python3-3.12.0b3'
|
|
these 3 paths will be fetched (65.51 MiB download, 219.36 MiB unpacked):
|
|
/nix/store/pxv7fa7ysw18kqrlvs1g0f9q66l7paz3-nodejs-18.16.1-libv8
|
|
/nix/store/3v1hjf626mh7mdii28m0srdbl8ch3dka-python3-3.12.0b3
|
|
/nix/store/b9a3j1rvcgj4wxpb30yzdi7ba62g3ha8-python3-3.12.0b3-debug
|
|
copying path '/nix/store/pxv7fa7ysw18kqrlvs1g0f9q66l7paz3-nodejs-18.16.1-libv8' from 'https://cache.nixos.org'...
|
|
copying path '/nix/store/b9a3j1rvcgj4wxpb30yzdi7ba62g3ha8-python3-3.12.0b3-debug' from 'https://cache.nixos.org'...
|
|
copying path '/nix/store/3v1hjf626mh7mdii28m0srdbl8ch3dka-python3-3.12.0b3' from 'https://cache.nixos.org'...
|
|
building '/nix/store/jid36dzng4pjqph3f0bdyzmsvaq5fl0h-user-environment.drv'...
|
|
```
|
|
|
|
And then do:
|
|
|
|
```
|
|
nix-env --install --remove-all vim
|
|
```
|
|
|
|
Only `vim` will be installed, and `python3` and `nodejs` are purged from
|
|
the environment. This is an important feature, as it allows us to
|
|
install any tools and versions we want to play around with `nix-env --install`, and then go back to scratch without worrying about leaving a
|
|
mess in our environments.
|
|
|
|
### Uninstalling packages from the environment
|
|
|
|
Uninstalling is without drama:
|
|
|
|
```
|
|
nix-env --uninstall vim
|
|
```
|
|
|
|
### Environment generations
|
|
|
|
Every time we use `nix-env` to modify our environment, Nix creates a new
|
|
generation. We can jump between generations, rollback, and delete them.
|
|
|
|
```
|
|
nix-env --rollback
|
|
```
|
|
|
|
This rolls back the current environment one generation, and is just a
|
|
convenience wrapper around `--list-generations` and
|
|
`--switch-generation`.
|
|
|
|
```
|
|
nix-env --list-generations
|
|
|
|
...
|
|
113 2023-06-21 16:39:06
|
|
114 2023-08-08 16:25:50
|
|
115 2023-08-08 16:36:01
|
|
116 2023-08-08 16:37:42 (current)
|
|
```
|
|
|
|
Using the id in the left column, we can jump to different generations of
|
|
our environment.
|
|
|
|
Deleting generations (they add up) can be done on a one-by-one basis, or
|
|
using smart values like `+5` (save last 5) and `30d` (save last 30
|
|
days).
|
|
|
|
```
|
|
nix-env --delete-generations 113
|
|
nix-env --delete-generations +5
|
|
nix-env --delete-generations 30d
|
|
```
|
|
|
|
## Where are we now ?
|
|
|
|
We have installed Nix (the package manager) and the Nix utilities
|
|
(`nix-*` commands) that we need.
|
|
|
|
We have explored how to create ad-hoc shell environments that only have
|
|
specific packages available, which is useful for trying out new things and
|
|
switching between versions of e.g. language runtimes.
|
|
|
|
We have learned how to manipulate the user's environment by installing
|
|
packages that persist in the environment (as opposed to the ephemeral
|
|
ones in `nix-shell`).
|
|
|
|
We've seen that Nix creates generations of the user's environments, and
|
|
that we can switch between generations or simply rollback the
|
|
environment to a previous state.
|
|
|
|
These are the building blocks for managing our user's environment, as
|
|
well as the various development environments that we strive for.
|
|
|
|
/v.
|