2025-01-01 03:36:12 -08:00
|
|
|
---
|
|
|
|
id: nixos-cheat-sheet
|
|
|
|
aliases: []
|
|
|
|
tags:
|
|
|
|
- nixos
|
|
|
|
- nixpkgs
|
|
|
|
- linux
|
2025-01-01 03:52:07 -08:00
|
|
|
title: Assorted NixOS hacks and tricks
|
2025-01-01 03:36:12 -08:00
|
|
|
---
|
|
|
|
|
|
|
|
The Nix package manager and NixOS Linux distribution are woefully
|
|
|
|
underdocumented. There are many, many powerful features that are difficult to
|
|
|
|
find, sans directly reading through source code.
|
|
|
|
|
|
|
|
I can't fix that, but this document serves as a personal cheat sheet of all the
|
|
|
|
small Nix features that I don't want to forget.
|
|
|
|
|
|
|
|
## Derivations
|
|
|
|
|
|
|
|
Stuff related to writing derivations of software.
|
|
|
|
|
|
|
|
### Sparse Checkout with fetchGit (and friends)
|
|
|
|
|
|
|
|
You can do sparse checkouts when using `fetchgit` and its friends
|
|
|
|
`fetchFromGitHub`, etc. This is useful if you're cloning a large repository and
|
|
|
|
know you only need one specific directory. If you only need a specific file,
|
|
|
|
consider `fetchurl`.
|
|
|
|
|
|
|
|
```nix
|
|
|
|
fetchgit {
|
|
|
|
url = "https://github.com/foo/bar";
|
|
|
|
hash = "sha256-0000000000000000000000000000000";
|
|
|
|
|
|
|
|
# takes a list of directories that should be checked out
|
|
|
|
sparseCheckout = ["dir", "another/dir", "foo/bar/dir"];
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
### The `callPackage` pattern
|
|
|
|
|
|
|
|
This isn't even a note for myself since I use this feature so much, but I'm add
|
|
|
|
it anyways so I can point to it as a reference.
|
|
|
|
|
|
|
|
When you look at `package.nix` files in the source code of `nixpkgs`, you'll
|
|
|
|
see they typically take the form:
|
|
|
|
|
|
|
|
```nix
|
|
|
|
{
|
|
|
|
stdenv,
|
|
|
|
libfoo,
|
|
|
|
libbar,
|
|
|
|
fetchFromGitHub,
|
|
|
|
...
|
|
|
|
}:
|
|
|
|
stdenv.mkDerivation {
|
|
|
|
nativeBuildInputs = [ stdenv libfoo libbar ];
|
|
|
|
|
|
|
|
src = fetchFromGitHub {
|
|
|
|
# blah blah
|
|
|
|
};
|
|
|
|
# rest of file omitted for brevity
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
It's a lambda (anonymous function) that takes in an attrset of everything the
|
|
|
|
derivation needs, and returns the derivation. As you clearly see, the `...`
|
|
|
|
means that the attrset contains all of the attributes specified, but also
|
|
|
|
arbitrarily more.
|
|
|
|
|
|
|
|
Exactly what attributes are available in this input attrset? It turns out that
|
|
|
|
this attrset is actually populated with the _entirety of nixpkgs_. Basically,
|
|
|
|
any package that can be referencd from `pkgs.xxx` is available in that input
|
|
|
|
attrset.
|
|
|
|
|
|
|
|
This is very useful for writing clean derivations. Oftentimes I see people
|
|
|
|
haphazardly pass `pkgs` around to all their derivations and directly make use
|
|
|
|
of it. While this is fine in small derivations, it quickly gets messy in large
|
|
|
|
ones. I prefer to always write non-trivial derivations using the nixpkgs
|
|
|
|
pattern. However, how do we go from a file like the example above to an actual
|
|
|
|
package that we can, say, output from a flake?
|
|
|
|
|
|
|
|
There's a function called `pkgs.callPackage` that handles precisely this task.
|
|
|
|
It takes in two arguments. Here is its pseudo type signature:
|
|
|
|
|
|
|
|
```
|
|
|
|
callPackage :: file -> attrset -> package
|
|
|
|
```
|
|
|
|
|
|
|
|
The first argument is the file that contains the derivation as shown in the
|
|
|
|
example. The second argument is an attrset that allows you to pass or overwrite
|
|
|
|
any values in the attrset passed to argument 1.
|
|
|
|
|
|
|
|
Oftentimes the second argument is not even needed and so you simply pass an
|
|
|
|
empty attrset (`{ }`).
|
|
|
|
|
|
|
|
```nix
|
|
|
|
packages.default = pkgs.callPackage ./my-derivation.nix { };
|
|
|
|
```
|
|
|
|
|
|
|
|
An example of the second argument in use is in a situation where you have a
|
|
|
|
flake that provides multiple packages, but one of these packages depends on the
|
|
|
|
other. Then, you can pass the packages to each other using the attrset
|
|
|
|
argument.
|
|
|
|
|
|
|
|
```nix
|
|
|
|
packages = rec {
|
|
|
|
foo = pkgs.callPackage ./foo.nix { inherit bar; };
|
|
|
|
bar = pkgs.callPackage ./bar.nix { };
|
|
|
|
};
|
|
|
|
```
|
|
|
|
|
|
|
|
Then, in the corresponding derivation of `foo`, `bar` will be available as an input:
|
|
|
|
|
|
|
|
```nix
|
|
|
|
# file: foo.nix
|
|
|
|
{
|
|
|
|
stdenv,
|
|
|
|
libblah,
|
|
|
|
bar, # the bar package we provided is now available here
|
|
|
|
...
|
|
|
|
}:
|
|
|
|
stdenv.mkDerivation {
|
|
|
|
buildInputs = [ bar ];
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
2025-01-01 03:50:02 -08:00
|
|
|
### Automatic updating derivations
|
|
|
|
|
|
|
|
Nix is powerful but also annoying at times. One of the most common issues is
|
|
|
|
how to automatically keep a derivation up to date.
|
|
|
|
|
|
|
|
When you fetch remote files in Nix, you need to provide Nix with a hash so that
|
|
|
|
it can guarantee the output is reproducible. This is a consequence of network
|
|
|
|
only being available in the "fixed output derivation".
|
|
|
|
|
|
|
|
When we automatically update a Nix derivation's sources, we need to take care
|
|
|
|
to both update the URL or revision and also the corresponding output hash.
|
|
|
|
|
|
|
|
The hash can be obtained using the `nix store prefetch-file` command, which
|
|
|
|
replaces the old and inferior `nix-prefetch-url`.
|
|
|
|
|
|
|
|
However this command doesn't return a clean hash that we can just pipe around
|
|
|
|
in our shell. We can add the `--json` flag and get a JSON object with the hash
|
|
|
|
in the `hash` property. Such as the following:
|
|
|
|
|
|
|
|
```nu
|
|
|
|
# this is a nushell script because i hate jq
|
|
|
|
nix store prefetch-file https://my-file.com/file --json | from json | get hash
|
|
|
|
```
|
|
|
|
|
|
|
|
We can then take our new URL and hash and then update our derivation. One way
|
|
|
|
to do this is with `sed` to modify the derivation in place, but this is kind of
|
|
|
|
janky. Another way is to create a JSON file that contains the URL and hash and
|
|
|
|
read it from Nix using `builtins.readFile` and `builtins.fromJSON`. This is
|
|
|
|
much easier since you can trivially generate JSON files using shell commands
|
|
|
|
(especially in Nushell).
|
|
|
|
|
|
|
|
The above process can be ran in CI to have fully automatic updating
|
|
|
|
derivations. See [[continuous-integration-in-nix-projects]] for writing on Nix
|
|
|
|
in CI.
|
|
|
|
|
2025-01-01 03:36:12 -08:00
|
|
|
## nixpkgs and the nixpkgs lib
|
|
|
|
|
|
|
|
General stuff related to quirks in nixpkgs and `lib`.
|
|
|
|
|
|
|
|
## Flakes
|
|
|
|
|
|
|
|
Niche features of Nix flakes.
|
|
|
|
|
|
|
|
### Nix flake inputs have additional properties
|
|
|
|
|
|
|
|
Everyone knows the common properties of Nix flake inputs: the standard outputs
|
|
|
|
like `packages`, `apps`, `nixosModules`, etc.
|
|
|
|
|
|
|
|
However, there a few that I have seen seldom used and hard to find mentions of
|
|
|
|
in documentation
|
|
|
|
|
|
|
|
```nix
|
|
|
|
{
|
|
|
|
inputs.cool-flake.url = "github:hackerman/cool-flake";
|
|
|
|
|
|
|
|
outputs =
|
|
|
|
{ self, cool-flake, ...}:
|
|
|
|
{
|
|
|
|
# gives the commit hash of the flake
|
|
|
|
rev = cool-flake.rev;
|
|
|
|
|
|
|
|
# gives the UNIX timestamp of the commit of the flake
|
|
|
|
lastModified = cool-flake.lastModified;
|
|
|
|
|
|
|
|
# you can also get these attributes on the current flake using `self`
|
|
|
|
|
|
|
|
# gets current git commit hash
|
|
|
|
# caveat: this property isn't defined if the git tree is dirty (there are uncommitted changes)
|
|
|
|
selfRev = self.rev;
|
|
|
|
|
|
|
|
# I often use this pattern due to the above reason
|
|
|
|
selfRev' = if (self ? rev) then self.rev else "FALLBACK"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
## NixOS
|
|
|
|
|
|
|
|
Things from the NixOS distribution, including the module system.
|
|
|
|
|
|
|
|
## Misc
|
|
|
|
|
|
|
|
Stuff to do with other stuff. Potentially community made flakes.
|
|
|
|
|
|
|
|
### Set the right time in Typix (and LaTeX)
|
|
|
|
|
|
|
|
When using Typix to compile Typst documents, sometimes it's a little _too
|
|
|
|
reproducible_. Attempting to reference the current date and time in the Typst
|
|
|
|
document will always return `January 1st, 1980` when compiled in the Nix build
|
|
|
|
environment.
|
|
|
|
|
|
|
|
You can set an environment variable to get the right time.
|
|
|
|
|
|
|
|
```
|
|
|
|
SOURCE_DATE_EPOCH = builtins.toString self.lastModified;
|
|
|
|
```
|
|
|
|
|
|
|
|
Here `self` is the self provided to the flake outputs that refers to the flake
|
|
|
|
itself. As shown in
|
|
|
|
[[./nixos-cheat-sheet.md#Nix-flake-inputs-have-additional-properties]], you can
|
|
|
|
get the timestamp of the current Git commit.
|