So this way i can have ready-to-use configurations like nixos-sinnenfreude-RELEASE and have them quickly applied through the deployment task that suffixes -install.
To the issue at hand i tried to implement a module that handles the configuration of hardware acceleration for the sinnenfreude system where my initial idea was:
Which is the projected ideal implementation as the case statement seems to make the implementation very easy to read and manage, but since it’s using config which it’s sourcing it causes infinite recursion… One method to manage this was to trick the nix interpreter by using mkIf as:
Which is harder to manage and it kinda causes more issues as on 24.05 it evaluates the 24.11 body of the if statement and fails because the option does not exists and vice-versa…
Question: Is there a sane and functional way to make release-flexible NixOS modules? I don’t want to manage this through making git branches as that makes the setup less functional – as separating branches inherently causes bitrotting as the older release implementation wouldn’t be getting updates due to added complexity to the maintainability where in this configuration i could just easily declare which options are to be always applied across all releases and global switches to handle the workflow.
I want to avoid declaring my own module options as it adds a huge amount of complexity that has to be constantly managed or how do you propose this should be integrated?
I want to basically make one module e.g. hardware-acceleration that will be responsible of handling hardware acceleration across multiple releases… doing that through multiple directories is major complexity in comparison to just having a file with case statement.
awww i got so hopeful… … feed that into system.nixos.release ?
so iiuc you are proposing to declare e.g. hardware.opengl.enable and do the configuration in a form of or conditionals? that seems like i would became unmaintainable very quickly considering scenario like hardware.opengl.enable changing on hardware.graphics.enable
It adds a lot of unwanted complexity, but maybe doing a wrapper that could do similar thing assuming that i just need to set the option so that nix doesn’t fail on it not being defined? Kinda running out of ideas tbh
Don’t do that. You might loose data because of that. Programs like nextcloud or postgres might just upgrade and data might still be laying in the wrong directory.
It always depends on what the issue at hand is. It’s often the case that some options simply get renamed and this pattern is a simple solution to those cases.
Also note that, in this example, you could also simply guard the other options behind the version check.
i.e.
{
config = mkMerge [
(lib.mkIf (!is2411) { # or lib.optionalAttrs if the options don't exist in the negative case
sound.enable = true;
hardware.pulseaudio.enable = false;
})
{
services.pipewire = {
enable = true;
alsa.enable = true;
alsa.support32Bit = true;
pulse.enable = true;
};
}
];
}
Well, any of this requires maintenance. You just have to use the lowest maintenance option available.
In the case of options that don’t exist yet where you just simply don’t care about setting it, mkSinkUndeclaredOptions can be the simplest option.
For instance, programs.steam.extraCompatPackages was added in 24.05. In order to make my config compatible, I simply sunk the option for systems using <24.05 where I didn’t care about it being set.
The reason why guards behind mkIf fail to eval when the options used don’t exist is that this is intentional: mkIf checks all options used, even if the condition is false in order to catch nasty bugs that depend on some condition.
If you know that you will intentionally use an option that would not pass the check on an older/newer version of NixOS and therefore guard it behind a version check that is never true when the option does not exist, NixOS does not know about this fact and will throw an error regardless. You can side-step this checking by making the attrs disappear entirely when the condition is false.
Which seems to be the projected ideal solution that manages the mentioned issues as using lib.trivial.release avoids the use of config that causes infinite recursion and gets me the desired string for comparison and doesn’t seem to evaluate the contents of the case bodies that do not match.