Skip to content

Latest commit

 

History

History
186 lines (156 loc) · 8.51 KB

README.md

File metadata and controls

186 lines (156 loc) · 8.51 KB

Combined Manager

Combined Manager provides a new structure for personal NixOS configurations.

Note: Requires patching nix to solve this issue. See more in the patching Nix section.

Introduction: No separation

The main feature of Combined Manager is to break separation. Most NixOS configuration structures are designed to separate related things into multiple files.

Most prominent separations:

  • Splitting your configuration into NixOS and Home Manager modules. These modules are then put into different files, even though they may be semantically related.
  • All flake inputs must be in flake.nix.

Combined Manager breaks this pattern by providing modules that can add inputs, import NixOS and Home Manager modules, and define Home Manager and NixOS options.

Module structure

{
  lib,
  inputs,
  pkgs,
  useHm, # Whether the current configuration uses Home Manager
  options,
  osOptions, # For [accessing NixOS option declarations](#accessing-option-declarations)
  hmOptions, # For [accessing Home Manager option declarations](#accessing-option-declarations)
  configs, # The results of all NixOS / Combined Manager configurations
  config,
  osConfig, # NixOS config
  hmConfig, # Home Manager config
  combinedManager, # The root of Combined Manager
  combinedManagerPath, # Path to the root of Combined Manager
  ...
}: {
  inputs = {name.url = "...";}; # Add inputs

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

  options = {}; # Declare Combined Manager options

  config = {
    inputs = {name.url = "...";}; # You can also add inputs here

    osModules = []; # You can also import NixOS modules here
    hmModules = []; # You can also import Home Manager modules here

    os = {}; # Define NixOS options

    hmUsername = "myname"; # Set the Home Manager username (must be defined if Home Manager is enabled for this configuration)

    hm = {}; # Define Home Manager options
  };
}

Accessing option declarations

The module system supports creating renamed options or creating aliases of options using functions like mkRenamedOptionModule or mkAliasOptionModule, but otherwise it is very hard to access option types or default values for more advanced use cases, as extracting them from the options arg is quite hard. An example of such a use case would be copying an option and changing the description, adding a default value, or slightly changing the type. If you need to conditionally include definitions, or generally reuse parts of an option declaration without simply forwarding definitions, you are out of luck using the module system. Combined Manager makes it easy and straightforward to access option declarations, even when working with types like attrsOf and submodules. It does this through the osOptions and hmOptions args. For example, the declaration for the option whose configuration you would access with os.some.option.path.submodule.name.option can be accessed with (osOptions.some.option.path"name").option.

Flake structure

let
  combinedManager = import (builtins.fetchTarball {
    url = "https://github.com/flafydev/combined-manager/archive/REV.tar.gz"; # Replace REV with the current revision of Combined Manager.
    sha256 = "HASH"; # Replace HASH with the corresponding hash.
  });
in
  combinedManager.mkFlake {
    description = "My flake description"; # Optional, defaults to "NixOS configuration"
    lockFile = ./flake.lock;

    initialInputs = {}; # Optional

    useHomeManager = true; # This sets a default that can be overridden per config. Defaults to true.

    # These are merged with the attributes specified per config. They are all optional.
    globalSpecialArgs = {};
    globalModules = [];
    globalOsModules = [];
    globalHmModules = [];

    # The NixOS configurations managed by Combined Manager.
    configurations = {
      primary = {
        # Attributes from the attrset created by this function override the normal inputs.
        # This can be used to override an input for a shared module on a per-configuration basis.
        inputOverrides = inputs: {}; # Optional

        useHomeManager = true; # If undefined, the mkFlake useHomeManager attribute is used, which defaults to true.

        specialArgs = {}; # The attributes defined in this attrset are passed to all Combined Manager, NixOS and Home Manager modules, in addition to the default args.

        # Add root Combined Manager, NixOS and home manager modules. These attributes are all optional.
        modules = [];
        osModules = [];
        hmModules = [];
      };

      # You can define as many configurations as you like.
      other = {};
    };

    # Here you can optionally define additional flake outputs. The additional outputs will be merged with the combinedManagerConfigurations and nixosConfigurations created by Combined Manager.
    outputs = {self, ...} @ inputs: {};
  }

Automatic updates

The following is an example of a flake.nix that automatically updates Combined Manager by specifying it as a flake input. Note that you must already have Combined Manager in your flake.lock for this to work, so make sure you add it as an input and run nix flake metadata before updating your flake.nix. If you accidentally delete flake.lock, you will need to hardcode rev and narHash before regenerating it, as the input evaluation relies on Combined Manager.

let
  inherit ((builtins.fromJSON (builtins.readFile ./flake.lock)).nodes.combined-manager.locked) rev narHash;
  combinedManager = import (
    builtins.fetchTarball {
      url = "https://github.com/Noah765/combined-manager/archive/${rev}.tar.gz";
      sha256 = narHash;
    }
  );
in
  combinedManager.mkFlake {
    lockFile = ./flake.lock;
    configurations = {};
  }

Current limitations

  • Only a single user supported when using Home Manager
  • Requires Nix to be patched
  • For NixOS configurations with flakes only

Getting started

  1. Patch Nix with one of the patches in the nix-patches directory. See more in the patching nix section.
  2. Use one of our flake templates with nix flake init -t github:FlafyDev/combined-manager#example, create your own using the docs, or take a look at an example to see a complete configuration using Combined Manager.
  3. If you want to use the osOptions or hmOptions args, make sure you specify your host platform (using nixpkgs.hostPlatform) in a NixOS module, such as hardware-configuration.nix. If you define it in a Combined Manager module, or not at all, these args won't work.
  4. See how Combined Manager can make your configuration cleaner!

Stability

At the time of writing, stable enough. While I'm using it for my configuraiton, I haven't tested everything and can't guarantee stability. There may be breaking changes.

Examples

Full configurations

Modules

Patching Nix

Because Combined Manager allows flake inputs to be distributed across multiple modules, which Nix doesn't support, it requires Nix to be patched. You can use one of the patches provided by this project, or alternatively use Nix Super.

Applying a patch to Nix

To apply the correct patch for the version of Nix on the unstable branch, add the following to your NixOS configuration:

nix.package = pkgs.nix.overrideAttrs (old: {
  patches =
    old.patches
    or []
    ++ [
      (pkgs.fetchurl {
        url = "https://raw.githubusercontent.com/flafydev/combined-manager/main/nix-patches/evaluable-flake.patch";
        hash = "HASH"; # Replace HASH with the hash of the patch you patch you want to apply.
      })
    ];
});

Once you're using Combined Manager, you can get the patch using the combinedManagerPath module arg:

nix.package = pkgs.nix.overrideAttrs (old: {
  patches = old.patches or [] ++ ["${combinedManagerPath}/nix-patches/evaluable-flake.patch"];
});