Skip to content

Flake Parts

The dendritic pattern uses flake-parts and is a modern pattern for organizing nix flakes. The documentation for it, like for everything else in the nix ecosystem, is totally disjointed, incomplete, and fucked generally.

From a High Level

Flakes already have an expected set of attribute names in the output schema, and flake-parts adds a few others. The important ones are packages, modules, nixosConfigurations, and homeConfigurations.

All the nix files in the configured directory are considered to also be flake-parts modules and are automatically imported by import-tree. This makes all the package and module names universally available.

Tools

hercules-ci/flake-parts
Provides the flake-parts attributes
vic/flake-file

Used to (re)generate the top-level flake file

Start new project
nix flake init -t github:vic/flake-file#default
(Re)generate nix inputs
nix run "$FLAKEDIR#write-flake"
vic/import-tree
import-tree recursively discovers and imports all Nix files in a directory. In Dendritic setups, where every file is Nix module (for instance flake-parts module) with the same semantic meaning, this eliminates manual import lists entirely.

Patterns

Module with Options

{ self, inputs, ... }:
{
  flake.modules.nixos.myModule = { config, pkgs, lib, ... }:
    let
      cfg = config.myModule; # (1)!
    in
    options.myModule = { # (2)!
      enable = lib.mkEnableOption "Description of my module";
      # Addtional module options here
      configDir = lib.mkOption = { # (3)!
        name = "Configuration directory";
        type = lib.types.str;
        default = "/etc/my-module";
      };
    };
    config = lib.mkIf cfg.enable {
      # Module config here
    }; 
  };
}
  1. This name has to match the one defined in the options attribute. It's just a convenient alias for this module to refer to its own options.
  2. This name has to match the one defined in the let/in block.
  3. These are all arbitrary example values