8000 GitHub - Noah765/modulix: Module system for the configuration of flake-based single user NixOS and Home Manager systems
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Noah765/modulix

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Modulix

Modulix provides a module system tailored for the configuration of flake-based single user NixOS and Home Manager systems. It also provides a flake generator that allows to decentralize flake inputs.

Introduction

The main advantage of Modulix is the elimination of the following separations:

  • Splitting your configuration into NixOS and Home Manager modules, which are typically put into different files, even though they may be semantically related.
  • Putting all flake inputs into flake.nix, even though they may only be relevant to one module.

Modulix provides a module system consisting of modules that can add flake inputs, import NixOS and Home Manager modules, and define NixOS and Home Manager options.

Another advantage of not using the NixOS or Home Manager module system directly is the freedom you gain when declaring your own options, because you don't have to worry about your options colliding with any NixOS or Home Manager options.

Getting started

  1. Create a config.nix file and fill it with attributes as described in the configuration structure section.
  2. Convert your NixOS and Home Manager configurations to Modulix modules. You can convert only parts of your existing configurations by importing the rest in osImports and hmImports.
  3. Generate your flake using the Modulix generator, or mxg, which this project's flake exposes as a package. You will need to regenerate it when your flake inputs change. For more information on the generator, see Modulix generator.
  4. Finally, you can build and activate your system as usual.

Module structure

{
  lib, # A slightly modified version of the nixpkgs library, extended with the Home Manager library
  utils, # Additional utility functions and constants, which are also available as an argument to NixOS modules
  inputs, # The flake inputs
  pkgs,
  configName, # The name of the configuration being evaluated
  useHm, # Whether the current configuration uses Home Manager
  hmUsername, # The Home Manager username, for which the configuration is being evaluated
  options,
  config,
  ...
}: {
  # Add inputs
  inputs = {
    name1.url = "...";
    name2.url = "...";
  };

  imports = [];
  osImports = []; # Import NixOS modules
  hmImports = []; # Import Home Manager modules

  options.someOption.enable = lib.mkEnableOption "..."; # Declare Modulix options

  config = {
    # Define assertions and warnings, just like with NixOS or Home Manager modules
    assertions = [
      {
        assertion = false;
        message = "Assertion message";
      }
    ];
    warnings = ["Warning message"];

    os.someOsOption = true; # Define NixOS options
    hm.someHmOption = true; # Define Home Manager options

    otherOption.enable = true; # Define Modulix options
  };
}

Configuration structure

The modulixSystems function exposed by this project's flake accepts configurations in the following form. Normally you don't need to call it directly, because flakes generated by the Modulix generator call it. For more advanced use cases, you can also use the nixosSystem function exposed by this project's flake to get the evaluated NixOS system for a single provided configuration.

{
  initialInputs.name.url = "..."; # Add flake inputs that do not exist in any module. nixpkgs, home-manager, and modulix are automatically added by the Modulix generator

  defaultSystem = "..."; # Set the default system. Defaults to x86_64-linux if not set
  defaultHmUsername = "..."; # Set the default Home Manager username

  globalModules = []; # Add Modulix modules, which are included in every Modulix configuration

  configurations = {
    config1 = {
      system = "..."; # Specify the system for this configuration. Defaults to the defaultSystem
      useHm = false; # Whether Home Manager should be enabled for this configuration. Defaults to true
      hmUsername = "..."; # Set the Home Manager username. Defaults to defaultHmUsername
      modules = []; # Add Modulix modules to this configuration
      prefix = []; # Change the prefix shown before option paths in error messages. Not necessary in most cases
    };
    config2.modules = [];
    config3.modules = [];
  };

  outputs = inputs: {}; # Add additional flake outputs
}

Suggested way to structure configurations

I would suggest structuring personal Modulix configurations as follows:

.
├── config.nix
├── hosts
│   ├── host1
│   │   ├── default.nix
│   │   └── hardware-configuration.nix
│   └── host2
│       ├── default.nix
│       └── hardware-configuration.nix
└── modules
    ├── default.nix
    ├── top-level-module.nix
    ├── category1
    │   ├── default.nix
    │   └── module.nix
    └── category2
        ├── default.nix
        ├── module1.nix
        ├── module2.nix
        └── subcategory
            ├── default.nix
            ├── module1.nix
            └── module2.nix
Visualization created with eza.
  • Within config.nix, create a configuration for each of your hosts.
  • Create a host directory for each of your hosts, containing the hardware-configuration.nix generated by nixos-generate-config.
  • Split your configuration into Modulix modules, where each module should be responsible for only one service that you might want to enable or disable for each of your hosts. The module should add flake inputs and import NixOS and Home Manager modules as needed.
  • Each Modulix module should reside in a different file, whose path in the file system should resemble the path to the options that module provides.
  • Each Modulix module should have at least one enable option.
  • Other parts of your config shouldn't directly define options that another module is responsible for. Instead, that other module should declare high-level options that other modules or hosts can then define.
  • For each depth in the modules tree, there should be a default.nix file that imports each module at that depth and sets defaults when it is enabled. This way, you benefit from nice defaults for each host, but you can also easily disable whole categories of modules if you need to.
  • The root of your module tree should be added to the globalModules parameter of your config.nix.

If your modules depend on each other and you want to express this easily, you can add the following module. To use it, just set the dependencies option to a list of string paths to other modules. String paths are similar to how you would access the definitions of it's declared options, e.g. category2.subcategory.module1.

{
  lib,
  options,
  config,
  ...
}: let
  inherit (lib) concatMap concatStringsSep drop flip getAttrFromPath mkOption removeSuffix splitString;
  inherit (lib.types) listOf str;
in {
  options.dependencies = mkOption {
    type = listOf str;
    internal = true;
    default = [];
    description = "Allows modules to express dependencies that must be enabled for the module to work properly. If the dependencies are not enabled, an error is thrown.";
  };

  config.assertions = flip concatMap options.dependencies.definitionsWithLocations (
    x: let
      dependent = removeSuffix ".nix" (concatStringsSep "." (drop 5 (splitString "/" x.file)));
    in
      flip map x.value (x: {
        assertion = getAttrFromPath (splitString "." x ++ ["enable"]) config;
        message = "${dependent} depends on ${x}, but ${x} is disabled.";
      })
  );
}

Modulix generator

Modulix provides a flake generator called Modulix Generator, or mxg for short, which this project's flake exposes as the mxg package. There is a need for this generator because nix requires flake inputs to be defined within the flake.nix file, see this issue.

Installation

You can add inputs.modulix.packages.${pkgs.system}.mxg to os.environment.systemPackages or hm.home.packages to expose the generator directly, or you can create your own rebuild script. The Modulix generator should be fast enough that it shouldn't have a noticeable impact on performance if it is run on every rebuild using a rebuild script. Here is a sample rebuild script using the Modulix generator and nh:

pkgs.writeShellScriptBin "rb" "${lib.getExe inputs.modulix.packages.${pkgs.system}.mxg} && ${lib.getExe pkgs.nh} os $* /etc/nixos"

Usage

The generator automatically searches all your Modulix modules and creates a flake.nix file with all the inputs it finds. It also automatically adds the nixpkgs, home-manager and modulix inputs if it doesn't find them, so you don't have to add them.

The generator accepts some optional options to tweak its behavior:

  • The generator normally doesn't overwrite flake.nix files that don't contain the autogeneration warning. You can change this with the --force or -f flag.
  • You can tell the generator which directory to find your configuration in by using the --path or -p option. This option defaults to /etc/nixos.

nixd support

Modulix supports option auto-completion using the nixd language server, using the following nixd configuration:

{
  "options": {
    "modulix": {
      "expr": "(builtins.getFlake \"LOCATION\").modulixConfigurations.NAME.options"
    }
  }
}

Replace LOCATION with your configuration location, e.g. /etc/nixos, and NAME with the name of a configuration you have defined in your config.nix. If you define the language server configuration inside a Modulix module, you can set NAME to the module argument configName.

Differences to Combined Manager

Modulix grew out of my fork of FlafyDev's Combined Manager project, so there are many similarities between the two projects. However, there are also some significant differences:

  • Combined Manager depends on a patched version of nix instead of a flake generator. Since there is no binary cache for this patched version of nix, you have to build nix locally whenever a new version of nix is released. Using a patched version also makes your configuration less easily reproducible.
  • Modulix has a cleaner and more streamlined approach to adding flake inputs, as well as NixOS and Home Manager modules, because it uses a slightly patched version of the nixpkgs library, allowing the module system to be extended with these new concepts.
  • Modulix supports option completion with nixd.
  • Modulix includes quite a bit of code to give you more informative error messages.
  • Modulix supports top-level assertions and warnings.

About

Module system for the configuration of flake-based single user NixOS and Home Manager systems

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

0