In my configuration, I am trying to build a “GUI enabled” module that will provide me with a single ‘enable’ switch to specify whether a machine has a GUI/Desktop or not. Then all my other modules can specify submodules to be included only if that single flag is enabled.
To be clear, the interface I’m trying to create is something like this:
# Somewhere in my top-level configuration file...
my.gui.enable = true;
# In another module managing command-line utilities...
environment.systemPackages = with pkgs; [ fish fzf htop tmux ];
# I want to add a terminal emulator, enable Flatpak, and set some nix options,
# but only if this is machine has a GUI
my.gui.modules = [{
envrionment.systemPackages = with; pkgs [ alacritty ];
services.flatpak.enable = true;
nix.gc.automatic = true;
}];
To this very similar to how I use home-manager’s ‘sharedModules’ option, so I tried following their code to inspire my ‘my.gui’ module:
{ config, pkgs, lib, ... }:
with lib;
let
cfg = config.my.gui;
in
{
options.my.gui = {
enable = mkEnableOption "GUI configurations";
modules = mkOption {
type = with type; listOf raw;
default = [];
};
guiConfig = mkOption = {
type = types.submoduleWith {
modules = cfg.modules;
};
default = {};
};
};
config = mkIf cfg.enable cfg.guiConfig;
}
This results in a recursion error, naturally. I’ve tried just about every permutation of mkIf and mkMerge that I could think of. I get that this is because one of the included modules could potentially set my.gui.enable = false;, which would cause a contradiction. Is there any work-around for this? For example, a way to enforce with types that the assembled guiConfig module can contain whatever attrs, but never a my.gui attribute?
I am currently using a setup where modules uses a mkIf and mkMerge like so:
config = mkMerge [
{
# Non-gui config...
}
( mkIf config.my.gui.enable {
# gui config...
})
];
This works, but it looks messy, and for the sake of cleanliness I am trying to refactor my configuration so that no module depends on the configuration of other modules (that is, the module’s output configuration can be entirely determined based on the values of its options).
Is there any way I can achieve the UX I am hoping for with these modules?