Denix

repository·master·Indexed 42 days ago

https://github.com/yunfachi/denix

A Nix library for building scalable, modular configurations for NixOS, Home Manager, and Nix-Darwin. It features a modular system based on Hosts, Rices, and Modules, allowing users to manage multiple target systems in a single file and switch visual themes (rices) quickly via flake outputs.

Tokens
19.4K
Snippets
62
Records
78
Agent score
53%

What's inside Denix

  1. Overview of Denix features

    master

    Denix is a Nix library designed for creating scalable and extensible configurations for NixOS, Home Manager, and Nix-Darwin. It uses a modular system to manage system-wide settings and machine-specific configurations.

    Key concepts include:

    • Modules: Custom modules that encapsulate options and related configurations for easy management.
    • Hosts and Rices: Hosts represent unique configurations for specific machines, while rices are customizations intended to be applied across all hosts.
    • Unified Configurations: You can write NixOS, Home Manager, and Nix-Darwin configurations within the same file; Denix automatically handles the separation.
    • Extensions: Denix supports built-in extensions that provide predefined modules and functions, and allows for the creation of custom extensions.
  2. What is Denix

    master

    Denix is a Nix library designed to create scalable configurations for NixOS, Home Manager, and Nix-Darwin. It provides functions that transform input data into modules using specific algorithms. This approach allows you to create modules using Denix and still maintain compatibility with standard Nix imports.

    Denix functions are categorized into five main areas:

    • Creating Flake configurations (for NixOS, Home Manager, or Nix-Darwin)
    • Options (analogues of types and functions for creating options from Nixpkgs)
    • Modules
    • Hosts
    • Rices
  3. What is a Denix Rice and how does it work?

    master

    In Denix, a 'rice' is a configuration attribute set returned by the delib.rice function. It represents a collection of system settings (often related to appearance) that can be applied to any host.

    When a rice is defined, it enables or disables specific configurations based on the value of the ${myconfigName}.rice option. Additionally, the input attribute set provided to delib.rice is passed to the ${myconfigName}.rice.${delib.rice :: name} option, allowing for fine-grained configuration within the rice itself.

    To use rices, you must define the ${myconfigName}.rice and ${myconfigName}.rices options in your modules. If you do not wish to use rices, simply omit these options and avoid using the delib.rice function.

    # Conceptual usage of delib.rice
    delib.rice {
      name = "my-rice-name";
      # ... configuration attributes
    }
  4. What is a Denix module?

    master

    A Denix module is a standard NixOS, Home Manager, or Nix-Darwin module that has been wrapped in the delib.module function. This wrapping allows the module to benefit from Denix's built-in logic for option declaration and configuration separation while remaining compatible with standard Nix module systems.

    {delib, ...}:
    delib.module {
      name = "...";
    }
  5. Understand Denix Options vs lib.mkOption

    master

    Denix options are a concise wrapper around the standard NixOS lib.mkOption. While you can always use lib.mkOption directly, Denix provides helper functions to reduce boilerplate and improve readability. For example, instead of writing a verbose lib.mkOption definition for a list of strings, you can use Denix's specialized functions.

    # Verbose lib.mkOption
    lib.mkOption {type = lib.types.listOf lib.types.str; default = [];}
    
    # Concise Denix equivalent
    delib.listOfOption delib.str []
  6. How to use inheritance in Denix Rices

    master

    Rices can inherit configurations from other rices using the inherits attribute, which accepts a list of rice names.

    You can also use the inheritanceOnly = true; attribute. This prevents the rice from being generated in delib.configurations, making it a private base rice used exclusively for providing shared configurations to other rices via inheritance.

    {delib, ...}:
    # A base rice used only for inheritance
    delib.rice {
      name = "rounded";
      inheritanceOnly = true;
      myconfig.hyprland.rounding = "6";
    }
    
    # A rice that inherits from 'rounded'
    delib.rice {
      name = "black";
      inherits = ["rounded"];
      myconfig.colorscheme = "black";
    }
  7. Key features of Denix modules

    master

    Denix modules provide several advantages over standard NixOS/Home Manager/Nix-Darwin modules:

    1. Simplified Option Declaration: Uses a simple yet fully functional way to declare options.
    2. Built-in Configuration Logic: Automatically handles configuration separation based on the value of ${delib.module :: name}.enable. Supported values are always, ifEnabled, and ifDisabled.
    3. Unified Options with Separated Configurations: Allows you to define shared options while maintaining distinct configurations for NixOS, Home Manager, Nix-Darwin, and custom options.
  8. Create custom modules with Denix

    master

    Denix simplifies the creation of Nix modules by providing a more readable syntax for options, config, and imports. Instead of manually using lib.mkEnableOption or lib.mkOption, you can use Denix's delib to define options more concisely.

    In a standard Nix module, you define options to declare variables and config to initialize them. Denix's delib.module abstraction wraps this logic, allowing you to define options using helper functions like boolOption and listOfOption, and apply configurations conditionally (e.g., using nixos.ifEnabled).

    {delib, ...}:
    delib.module {
      name = "example";
    
      options.example = with delib; {
        enable = boolOption true;
        hostnames = listOfOption str [];
      };
    
      nixos.ifEnabled = {cfg, ...}: {
        networking.hosts."127.0.0.1" = cfg.hostnames;
      };
    }
  9. Configure multiple Nix channels in Denix

    master

    To manage multiple channels (e.g., mixing nixpkgs stable and unstable), you can pass nixpkgs, home-manager, and nix-darwin as arguments to the delib.configurations function.

    By default, these arguments use the inputs provided to the Denix flake. If you only use a single channel, you can simplify your setup by using the follows keyword in your flake inputs to ensure all components align with your primary nixpkgs version.

  10. How Denix generates configuration lists

    master

    The delib.configurations function automatically generates lists of nixosConfigurations, homeConfigurations, and darwinConfigurations for Flakes.

    It creates configurations for every host defined in your paths, and for every host, it also creates a combination with every rice that has inheritanceOnly = false. This allows you to switch between different visual styles (rices) without modifying your host code.

    Naming Convention:

    • {hostName}: The base configuration for a host.
    • {hostName}-{riceName}: A configuration for that host using a specific rice (where riceName is a rice with inheritanceOnly = false).

    Home Manager Prefix: If the moduleSystem argument is set to "home", all generated configurations in the list will be prefixed with {homeManagerUser}@.

    nixos-rebuild switch --flake .#desktop-dark --use-remote-sudo
  11. How Denix modularity and configuration works

    master

    Denix uses a modular system to manage NixOS, Home Manager, and Nix-Darwin configurations through three main abstractions:

    1. Modules: Custom modules allow you to define options and related configurations flexibly.
    2. Hosts: Unique configurations tailored for specific machines.
    3. Rices: Customizations (visual or functional) that can be applied across all hosts.

    One of the core benefits of Denix is the ability to write NixOS, Home Manager, and Nix-Darwin configurations in a single file; Denix automatically handles the separation of these configurations for you.

  12. Split module logic into multiple files

    master

    You can split a single logical module (sharing the same name) across multiple files to keep configurations organized. For example, a Hyprland module can be split into default.nix (for core setup), settings.nix (for general configuration), and binds.nix (for keybindings).

    Each file must use the same name property within delib.module to be treated as part of the same logical module.

    # settings.nix
    {delib, ...}:
    delib.module {
      name = "programs.hyprland";
    
      home.ifEnabled.wayland.windowManager.hyprland.settings = {
        "$mod" = "SUPER";
        general = {
          gaps_in = 5;
          gaps_out = 10;
        };
      };
    }
    
    # binds.nix
    {delib, ...}:
    delib.module {
      name = "programs.hyprland";
    
      home.ifEnabled.wayland.windowManager.hyprland.settings = {
        bindm = [
          "$mod, mouse:272, movewindow"
          "$mod, mouse:273, resizewindow"
        ];
    
        bind = [
          "$mod, q, killactive,"
          "CTRLALT, Delete, exit,"
          "$mod, Return, exec, kitty"
        ];
      };
    }