Comparing module system configurations

Background

I want to try out nixos-facter. I want to replace my hardware-specific option values in some configuration with usage of nixos-facter, and then examine the resulting differences. So I want the ability to compare two NixOS configurations.

Derivation-level Comparison

I did try using nix-diff on the derivations obtained via <my-configuration>.config.system.build.toplevel and of course it did show me the changes betweeen the two derivations. Which is useful and sometimes all one needs.
But what I really want in this case, is a higher level view. A view on the changes between the configurations. Well, at least that’s what I think I want. It’s hard to tell before I take a good look at such a comparison.

What is a configuration?

To the best of my understanding, a configuration is an evaluated set of modules. For example, a NixOS configuration is obtained in such a manner:

let
  evaluated =  nixpkgs.lib.nixosSystem {
    modules = [
      oneModule
      anotherModule
    ];
  };
in
  evaluated.config

Kind of.

So just serialize them and compare

Using builtins.toJSON. Well, you can’t just do that, because:

  1. This value also includes functions, for some reason. So these can be filtered away or mapped to strings.
  2. This value also includes derivations. The attempt to serialize these results in building them. Okay, so map them to their .drvPath, instead.
  3. Some values included are never evaluable, because the expressions simply always throw. For example, renamed options, I suppose. This can be worked-around using builtins.tryEval.

We’re stuck at attempts to access non-existent attributes

The options for some values are meant to be accessed/evaluated only when some other option has some value. For example, foo.contents is meant to be evaluated only if foo.enable is true. To the effect that the value of foo.contents is an expression that would fail to evaluated with a missing attribute error otherwise. And missing attribute errors such as {}.a are not caught by builtins.tryEval, it seems. And I don’t see any other Nix language feature that can handle such an error.

How to proceed?

Does anyone else want to compare configurations? Or to serialize or just deeply evaluate a configuration for some purpose? Is Nixpkgs virtually full of these options that must not be evaluated unless some other option has some value? Are we missing something?

Mentions

A previous post of mine about this

Co-authored-by @A-jay98

2 Likes

I was looking for the same thing when I was doing a major refactor of my config. I had to do it in really small steps and check if the results end up with the same hash.

1 Like

Just stumbled across nixos-option in another thread. I had a quick check yesterday and ran into an issue with config.assertions but I think that is a bug in nixpkgs. In recursive mode the output should be diffable.

I have to play a bit more to really check if it fits my use case. It might even be a starting point for something else. I would rather have json output.