How to check is an option available?

I have a difficulty writing configuration.nix which suppose to work with many NixOS releases, for example a test-driver which is suitable for bisecting a bug:
NixOS options appear and disappear with time.
And it is not easy to check if an option is available

For example, networking.networkmanager.dns appeared approx. in NixOS 18.09
It works fine with release-18.09 but as we go back in time to release-18.03 it stops working.

  1. networking.networkmanager.dns ="none"; fails with “there is no such option”.

  2. mkIf ... { networking.networkmanager.dns = "none"; } fails too with “there is no such option”

  3. lib.optionalAttrs (config.options?networking.networkmanager.dns) { networking.networkmanager.dns = "none"; } fails with infinite recursion

  4. lib.optionalAttrs (lib?bitXor) { networking.networkmanager.dns = "none"; } finally works because lib.bitXor appeared approximately at the same time and accessing lib. does not result in infinite recursion

But it is weird and unreliable, it might be good enough to distinguish release-18.03 and release-18.09 but config.options?networking.networkmanager.dns and lib?bitXor point to different moments in nixpkgs history and the problem will strike back if bisecting approach them

1 Like

didn’t test this. But you can make your config take options as an argument and use that instead of config.options ?
maybe that’s lazy enough.

{ options, config, lib, ...}: with lib;
mkMerge [
  mkIf options.networkmanager?dns {
    networking.networkmanager.dns = "none";
  }
]

Also: TIL that you can use ? for attrset checking

Edit: nope, same error alas

Aha, mkIf pushes down the condition, to do this:

{ options, config, lib, ...}: with lib;
mkMerge [
  networking.networkmanager.dns = if options.networkmanager?dns "dns" else null
]

which indeed is not gonna work. But this works!

{ options, config, lib, ...}: with lib;
yourconfig // (if options.networkmanager?dns {
  networking.networkmanager.dns = "none";
}
)

Or indeed this:

{lib, options}:
lib.optionalAttrs (options?networking.networkmanager.dns) { 
    networking.networkmanager.dns = "none";
}

So the trick was to use options instead of config.options

2 Likes