284 lines
4.7 KiB
Markdown
284 lines
4.7 KiB
Markdown
# Nix Conventions
|
|
|
|
This document records local Nix style and design conventions.
|
|
Use these conventions when changing this flake.
|
|
Read this document with `docs/dictionary.md`.
|
|
|
|
## Names
|
|
|
|
### Reserve `lib` for `nixpkgs.lib`
|
|
|
|
Use `lib` only for the base `nixpkgs.lib` value.
|
|
Do not use `lib` for project functions.
|
|
Use `dotfiles.lib` for library functions passed through module arguments.
|
|
Use `dotfiles.<name>` for assembly functions passed through module arguments.
|
|
|
|
Avoid:
|
|
|
|
```nix
|
|
{
|
|
lib = dotfilesFunctions;
|
|
}
|
|
```
|
|
|
|
Use:
|
|
|
|
```nix
|
|
{
|
|
lib = nixpkgs.lib;
|
|
dotfiles.lib = dotfilesFunctions;
|
|
}
|
|
```
|
|
|
|
### Name project function namespaces by role
|
|
|
|
Put pure project functions under `dotfiles.lib`.
|
|
Put functions that create modules, systems, or assembly values under `dotfiles`.
|
|
This keeps data-only functions separate from functions that carry flake context.
|
|
|
|
Avoid:
|
|
|
|
```nix
|
|
dotfiles.lib.mkHost { hostName = "neogaia"; }
|
|
```
|
|
|
|
Use:
|
|
|
|
```nix
|
|
dotfiles.mkHost { hostName = "neogaia"; }
|
|
```
|
|
|
|
## Function Arguments
|
|
|
|
### Use closed interfaces for assembly files and library files
|
|
|
|
Use exact function argument patterns for assembly files and library files.
|
|
The caller owns the argument set for these files.
|
|
The imported file names each accepted attribute.
|
|
The imported file rejects stale or accidental extra attributes.
|
|
|
|
Avoid:
|
|
|
|
```nix
|
|
{
|
|
inputs,
|
|
lib,
|
|
self,
|
|
...
|
|
}:
|
|
```
|
|
|
|
Use:
|
|
|
|
```nix
|
|
{
|
|
inputs,
|
|
lib,
|
|
self,
|
|
}:
|
|
```
|
|
|
|
### Pass function arguments explicitly
|
|
|
|
Pass only the values an assembly file or library file needs.
|
|
Bind `lib` to the base `nixpkgs.lib` value at the call site.
|
|
Use `inherit` only for values that keep the same name.
|
|
|
|
Avoid:
|
|
|
|
```nix
|
|
import ./flake inputs
|
|
```
|
|
|
|
Use:
|
|
|
|
```nix
|
|
import ./flake {
|
|
inherit inputs self;
|
|
lib = nixpkgs.lib;
|
|
}
|
|
```
|
|
|
|
### Keep NixOS module interfaces open
|
|
|
|
Files under `hosts/`, `guests/`, and `modules/` return NixOS modules.
|
|
The NixOS module system owns their argument shape.
|
|
Name each argument the module uses.
|
|
Keep `...` so the module accepts standard module arguments and special arguments.
|
|
|
|
Avoid:
|
|
|
|
```nix
|
|
{
|
|
config,
|
|
lib,
|
|
pkgs,
|
|
registry,
|
|
}:
|
|
```
|
|
|
|
Use:
|
|
|
|
```nix
|
|
{
|
|
config,
|
|
lib,
|
|
pkgs,
|
|
registry,
|
|
...
|
|
}:
|
|
```
|
|
|
|
### Use `@` for forwarding wrappers
|
|
|
|
Use `@` when a wrapper passes the complete argument set to another module function.
|
|
Name each argument the wrapper uses before `...`.
|
|
Forward the bound argument set unchanged.
|
|
|
|
Avoid:
|
|
|
|
```nix
|
|
{ dotfiles, ... }:
|
|
dotfiles.guest { name = "sample"; } { inherit dotfiles; }
|
|
```
|
|
|
|
Use:
|
|
|
|
```nix
|
|
args@{ dotfiles, ... }:
|
|
dotfiles.guest { name = "sample"; } args
|
|
```
|
|
|
|
## `let` Bindings
|
|
|
|
### Use `let` for named intermediate values
|
|
|
|
Use a `let` binding when it names an important intermediate value.
|
|
Use a `let` binding when it avoids repeated complex expressions.
|
|
Do not use a `let` binding only to shorten a standard library call.
|
|
|
|
Avoid:
|
|
|
|
```nix
|
|
let
|
|
inherit (lib) mapAttrs;
|
|
in
|
|
mapAttrs f attrs
|
|
```
|
|
|
|
Use:
|
|
|
|
```nix
|
|
lib.mapAttrs f attrs
|
|
```
|
|
|
|
### Keep `let` scopes local
|
|
|
|
Place a `let` binding near the expression that needs it.
|
|
Do not lift a value to a larger scope only for convenience.
|
|
A larger scope makes the dependency look more important than it is.
|
|
|
|
Avoid:
|
|
|
|
```nix
|
|
let
|
|
hostNames = lib.attrNames hosts;
|
|
in
|
|
{
|
|
names = hostNames;
|
|
count = lib.length hostNames;
|
|
}
|
|
```
|
|
|
|
Use:
|
|
|
|
```nix
|
|
{
|
|
names = lib.attrNames hosts;
|
|
count = lib.length (lib.attrNames hosts);
|
|
}
|
|
```
|
|
|
|
## Standard Library Calls
|
|
|
|
### Qualify standard library calls in flake assembly
|
|
|
|
Use qualified standard library calls inside flake assembly code.
|
|
Use `lib.mapAttrs` instead of inheriting `mapAttrs` from `lib`.
|
|
This keeps the source of each function visible.
|
|
|
|
Avoid:
|
|
|
|
```nix
|
|
let
|
|
inherit (lib) mapAttrs;
|
|
in
|
|
mapAttrs f attrs
|
|
```
|
|
|
|
Use:
|
|
|
|
```nix
|
|
lib.mapAttrs f attrs
|
|
```
|
|
|
|
## Flake Assembly
|
|
|
|
### Keep assembly code under `flake/`
|
|
|
|
Keep flake assembly code under `flake/`.
|
|
Keep `flake.nix` focused on inputs and public outputs.
|
|
The `outputs` function returns each public output as an attribute.
|
|
An external flake reads public outputs through `inputs.<name>.<output>`.
|
|
`nix flake show` displays public outputs.
|
|
|
|
Avoid:
|
|
|
|
```nix
|
|
# flake.nix
|
|
let
|
|
mkHosts = hostsDir: ...;
|
|
in
|
|
{
|
|
nixosConfigurations = mkHosts (self + "/hosts");
|
|
}
|
|
```
|
|
|
|
Use:
|
|
|
|
```nix
|
|
# flake.nix
|
|
let
|
|
flakeAssembly = import ./flake {
|
|
inherit inputs self;
|
|
lib = nixpkgs.lib;
|
|
};
|
|
in
|
|
{
|
|
nixosConfigurations = flakeAssembly.mkHosts (self + "/hosts");
|
|
}
|
|
```
|
|
|
|
### Expose only supported flake outputs
|
|
|
|
Use public outputs for the supported interface of this flake.
|
|
Do not expose assembly functions as public outputs unless an external consumer needs them.
|
|
Keep internal functions in assembly files or `dotfiles` special arguments.
|
|
|
|
Avoid:
|
|
|
|
```nix
|
|
{
|
|
lib = flakeAssembly;
|
|
nixosConfigurations = flakeAssembly.mkHosts (self + "/hosts");
|
|
}
|
|
```
|
|
|
|
Use:
|
|
|
|
```nix
|
|
{
|
|
nixosConfigurations = flakeAssembly.mkHosts (self + "/hosts");
|
|
}
|
|
```
|