mkMerge as the body of a configuration?

I made a function to generate an import function for a NixOS configuration. I needed to deep-merge several attribute sets, and so I resorted to something like

{ config, pkgs, lib, ... }: lib.mkMerge [
  { imports = [ ./something.nix ]; services.foo.enabled = true; }
  (lib.mkIf useCifs { services.foo.bar = 7; })
  # ...
]

(simplified for illustration purposes)

Evaluating this with NixOps gave me an error like “imports is not a configuration option”. I tried combining deep an shallow merging but that just moved the problem.

In the end I resolved it by doing

{ config, pkgs, lib, ... }: {
  imports = [
    ./something.nix
    ({ config, pkgs, lib, ... }: lib.mkIf useCifs { services.foo.bar = 7; })
  ];
  services.foo.enabled = true;
}

It seems that the NixOS configuration can’t be a {_type: 'merge', Contents: ...} object?

(I’m typing this from memory, I’m just curious)

2 Likes

NixOS modules have three (main) sections: imports, options and config. And mkMerge only works on the config section, as do mkIf, mkForce and co. So you can’t use any of these modifiers on the imports or the options section (and if you try, you’ll get errors as if imports and options are missing options).

Note however that if a NixOS module doesn’t have either a config or options section, the whole thing is assumed to be a config section. So e.g. this works:

{ lib, ... }: mkMerge [{
  environment.systemPackages = [ ... ];
}]

Because it implicitly gets turned into

{ lib, ... }: {
  config = mkMerge [{
    environment.systemPackages = [ ... ];
  }];
}
9 Likes