nh icon indicating copy to clipboard operation
nh copied to clipboard

Module option for Specialisations

Open viperML opened this issue 7 months ago • 4 comments

To declare specialisations and make them work with nh, we must use the following:

{config, pkgs, ...}: {
  specialisation."foo" = {
    configuration = {
      environment.etc."specialisation".text = "foo";
      # ..rest of config
    };
  };
}

It would be nice to have a wrapper, such that I don't forget to do it:

{config, pkgs, ...}: {
  programs.nh.specialisation."foo" = {
    inheritParentConfig = true;
    configuration = { /* ... */ };
  };
}

viperML avatar May 05 '25 07:05 viperML

What kind of an API do you have in mind for this? Does nh create those files by initiating a command on first run (and skip creating on subsequent runs, etc.) or is this a nixos module thing?

NotAShelf avatar May 05 '25 08:05 NotAShelf

Just injecting environment.etc.<name>.text = name; in the specialisation config.

viperML avatar May 05 '25 08:05 viperML

So just through the module system, alright. Should be relatively easy to map nh specializations.

NotAShelf avatar May 05 '25 08:05 NotAShelf

It’s not very obvious at first, but you can centralize this logic into a single module that handles it for all specialisations. The key is to extend the configuration submodule under options.specialisation, rather than extending specialisation.<name>.configuration directly in config. The latter quickly leads to infinite recursion.

Instead, do something like this:

{ lib, ... }:

let
  inherit (lib) mkOption types;

  named-spec-module = spec: { ... }: {
    config = {
      environment.etc."specialisation".text = spec;
    };
  };
in

{
  options.specialisation = mkOption {
    type = types.attrsOf (types.submodule (
      { name, ... }: {
        options.configuration = mkOption {
          type = types.submoduleWith { modules = [ (named-spec-module name) ]; };
        };
      }
    ));
  };
}

It’s not entirely clear to me why the relevant modules in Nixpkgs don’t already inject the attribute names into their corresponding configs. Specialisations do get a name argument in their module scope, but it’s not all that helpful as it's the name of an attribute in a different set. Classic Nix, haha.

SimSaladin avatar May 07 '25 04:05 SimSaladin