Last active
February 26, 2023 18:51
-
-
Save jfredett/e345bd3393e9b52e2c54b485f3d4b580 to your computer and use it in GitHub Desktop.
NixOS Parameterized Module Issue
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
unpacking channels... | |
error: infinite recursion encountered | |
at /nix/var/nix/profiles/per-user/root/channels/nixos/lib/modules.nix:256:21: | |
255| (regularModules ++ [ internalModule ]) | |
256| ({ inherit lib options config specialArgs; } // specialArgs); | |
| ^ | |
257| in mergeModules prefix (reverseList collected); | |
(use '--show-trace' to show detailed location information) | |
building Nix... | |
error: infinite recursion encountered | |
at /nix/var/nix/profiles/per-user/root/channels/nixos/lib/modules.nix:256:21: | |
255| (regularModules ++ [ internalModule ]) | |
256| ({ inherit lib options config specialArgs; } // specialArgs); | |
| ^ | |
257| in mergeModules prefix (reverseList collected); | |
(use '--show-trace' to show detailed location information) | |
building the system configuration... | |
error: infinite recursion encountered | |
at /nix/var/nix/profiles/per-user/root/channels/nixos/lib/modules.nix:256:21: | |
255| (regularModules ++ [ internalModule ]) | |
256| ({ inherit lib options config specialArgs; } // specialArgs); | |
| ^ | |
257| in mergeModules prefix (reverseList collected); | |
(use '--show-trace' to show detailed location information) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ config, lib, pkgs, ... }: | |
let | |
ec = (import <nix/common/eclib.nix>); | |
familyName = "pinky"; | |
in ec.template.mkParamterizedModule { | |
inherit familyName; | |
childrenName = "subservices"; | |
parentOptions = { | |
enable = lib.mkEnableOption "pinky test service"; | |
name = lib.mkOption { type = lib.types.str; default = "${familyName}-family"; }; | |
}; | |
childOptions = { | |
startScript = lib.mkOption { type = lib.types.str; }; | |
stopScript = lib.mkOption { type = lib.types.str; }; | |
reloadScript = lib.mkOption { type = lib.types.str; default = ""; }; | |
user = lib.mkOption { type = lib.types.str; default = "root"; }; | |
group = lib.mkOption { type = lib.types.str; default = "root"; }; | |
}; | |
mkParent = opts: {}; # not used | |
mkChild = name: cfg: { | |
systemd = { | |
# to be clear, this content doesn't matter, the id function here also causes the issue. | |
services.${name} = { | |
enable = true; | |
description = "Pinky Test Service: ${name}"; | |
after = [ "multi-user.target" ]; | |
wants = [ "${name}.timer" ]; | |
serviceConfig = { | |
ExecStart = pkgs.writeShellScript "${name}-start.sh" cfg.startScript; | |
ExecStop = pkgs.writeShellScript "${name}-stop.sh" cfg.stopScript; | |
ExecReload = lib.mkIf (cfg.reloadScript != "") (pkgs.writeShellScript "${name}-reload.sh" cfg.reloadScript); | |
Type = "oneshot"; | |
User = cfg.user; | |
Group = cfg.group; | |
}; | |
}; | |
timers.${name} = { | |
enable = true; | |
description = "Pinky Test Service Timer: ${name}"; | |
after = [ "multi-user.target" ]; | |
partOf = [ "${name}.service" ]; | |
timerConfig.OnCalendar = "*:*:0,15,30,45"; | |
}; | |
}; | |
}; | |
} { inherit config lib pkgs; } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
mkParameterizedModule = { | |
familyName, | |
childrenName, | |
parentOptions, | |
childOptions, | |
mkParent, | |
mkChild | |
}: | |
{ config, pkgs, lib, ...}: with lib; let | |
parentConfig = mkParent parentConfigOptions; | |
childrenConfig = mapAttrs mkChild childConfigOptions; | |
mkChildren = cfg: mapAttrs mkChild cfg; | |
confs = (parentConfigOptions: childConfigOptions: foldl' (a: v: a // v) (mkParent parentConfigOptions) (mkChildren childConfigOptions)); | |
in { | |
options.${familyName} = parentOptions // { | |
${childrenName} = mkOption { | |
type = types.listOf (types.submodule childOptions); | |
}; | |
}; | |
config = mkIf parentConfigOptions.enable (confs config.${familyName} config.${familyName}.${childrenName}); | |
# ^^^^^ this and ^^^^ this | |
# are the offending lines that cause an infinite recursion, but | |
# I'm confused as to why, since it's normal to refer to options | |
# like this in a 'regular' module. | |
}; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi! I'm back!
I figured this out -- well, I figured out a bad way to do it that works, and ultimately I think I learned that Nix is not a language that really wants you to encode higher-order things in it. Honestly, the entire exercise has left me thinking that what I'm looking for is a language that 'compiles' (for some value of the word 'compile') to Nix but allows for encoding patterns like this in a nicer way. Someday maybe I'll build that thing I want.
In the meantime, the trick is to just tell Nix to merge the
config
key one level at a time. Like this:This works fine, you can see it in action here, and eventually in the repo that gist links too. This is, of course, ugly, but it does work and avoids the infinite recursion. The
mkOneLevelTree
function in the linked gist does what I wanted, but ironically, it turns out I really needed a two-level tree, which is the same basic problem and could be adapted from the linked code, but I'm coming around to the notion that that doesn't really improve things in the way I hoped.For future readers, I'm unlikely to return to this topic again, I suppose if you have specific questions about the series of breaks with reality that led to this delightful mess, just @ me and I'll try to respond. For now, I'm going to go out to the woods and think about what I've done. I'm very sorry.