Nix eval to read a value or provide a default

The simple explanation

I have a program that’s going to read my configuration from flake.nix and do something with the remote host based on some information in there.

I can read a value from the Nix flake like this:

nix eval --read-only --quiet .#nixosConfigurations.my-host.config.my-feature.enabled

This works great. Nice and fast too. Returns true or false, based on what that value is set to.

The problem: I want it to default to false if the value is not defined at all (it’s defined in a module, so I want it to assume false if the module is not present). Currently nix eval just returns a non zero error code, so I can’t tell if it wasn’t defined or if there was a syntax error.

The icky details

I’m working on an experimental system for managing robots.
I’ve created this module to automatically revert to a previous generation if the robot doesn’t get confirmation that ssh still works. I want to make my deployment system get grumpy at the user if this module is disabled or not present.

You might be able to use --apply with something involving tryEval?

Can’t tell what you want, seems like there’s conflicting requirements here.

Use options.<whatever>.isDefined to check if it’s set.
Or provide default to mkOption.

@waffle8946 I’m confused by that answer…
It is defined as an option, with a default here.

The issue is that if the user doesn’t include the module, then that option, with its default, will not be present and the evaluation fails.

@tejing Is this what you had in mind? Looking at the documentation for tryEval, I’m guessing this doesn’t count as a “thrown error”.

[thecarl@the-carl-tower:~/Projects/ros_assistant/packages/rass-cli/example_projects/nvidia-xavier-agx]$ nix eval --read-only --apply builtins.tryEval .#nixosConfigurations.generic-x86.config.auto-revert.enable
error: flake 'git+file:///home/thecarl/Projects/ros_assistant?dir=packages/rass-cli/example_projects/nvidia-xavier-agx' does not provide attribute 'packages.x86_64-linux.nixosConfigurations.generic-x86.config.auto-revert.enable', 'legacyPackages.x86_64-linux.nixosConfigurations.generic-x86.config.auto-revert.enable' or 'nixosConfigurations.generic-x86.config.auto-revert.enable'

Guess that idea won’t work.

You could still use --apply, though. Just call it on nixosConfigurations.generic-x86.config and index into the attrset using the --apply expression, with defaults, and probably still tryEval, for the case where the option is created but never set, since the module system should throw in that case.

I don’t think I follow.

Is this in the ballpark of what you’re thinking?

nix eval --read-only --apply builtins.tryEval .#nixosConfigurations.generic-x86.config

I’m starting to suspect what I’m trying to do may just be too far outside of the scope of Nix and I should rethink the whole idea. The ultimate goal is to detect if the auto-revert.nix module has been included into the configuration.

More like

nix eval --read-only --apply 'config: (config.my-feature or false).enabled or false' .#nixosConfigurations.generic-x86.config

It sounds like you don’t need to catch a “not-set” error, since you provide a default value so long as the module is loaded at all. If that’s correct, then you shouldn’t need the tryEval after all.

Thank you so much for your time and patience! I had to massage it a little but that seems to do exactly what I need.

I had tried to do something similar to this but hadn’t figured out that making it a function would let me use it under apply (I still consider myself new to Nix). I’m just trying to make sure I grasp exactly how this works.

#                                      |- If this fails to be resolved, false
#                                      |                           |- Just kicks back the actual value of "enabled"
#                                      |                           |          |- If enabled failed to resolve, false
#                                      |                           |          |      | - Path to our system's config. Is used as the input to the function.
#                                      V                           V          V      V
nix eval --read-only --apply 'config: (config.my-feature or false).enabled or false' .#nixosConfigurations.generic-x86.config

Yup, you’ve got that about right. Technically the first false is a bit misleading, since it isn’t actually the return value in the case where my-feature doesn’t exist. It just ensures the second lookup fails because false isn’t an attrset and thus doesn’t have any keys. You could replace the first false with {} and it might be a little clearer how that part works.

1 Like