Just to get some eyes / discussion on this ![]()
Iāve read https://nixos.org/manual/nixos/stable/options#opt-_module.args but Iām still a bit confused. Iām sure from some of the comments that this is useful, but I donāt quite understand specifically how. Can someone provide a reproducible example of something that would work with specialArgs but not _module.args and explain why that use case is desirable? Thanks!
Thatās covered off by cover letter to the PR, but to make it concrete:
let
sources = import ./npins;
nixos = import (sources.nixpkgs + "/nixos")
in
nixos {
configuration.imports = [
{ _module.args = { inherit sources; } }
{ sources, ... }: {
import = [ sources.modules ];
}
];
}
will fail to build with an infinite recursion error. If sources are passed by specialArgs it will build just fine.
Basically the issue is, imports needs to be built-up statically.
If you use specialArgs to pass in something thatās used by imports, thereās only one entry-point for that stuff (the entry-point for the module system evaluation itself), so future modules cannot modify it.
But if you use _module.args, thatās part of a module, so another module could modify it, leading to an infinite recursion between trying to build up the imports list and said list depending on the modules it pulls in⦠I hope you see how thatās circular.
Still, specialArgs should be considered the last resort and is often easily worked around.
Thanks for additional context. My intuition does lead me to believe it would be circular, but Iāve had to somewhat stop relying on my intuition for what is circular when it comes to Nix and lazy evaluation, unfortunately. Iām sure it will get better with time. My first topic posted in this discourse was How is nixpkgs and the NixOS module system avoiding infinite recursion? (With specific code excerpt) - #5 by withakay haha.
Thank you so much. Making it concrete really helps when the concepts arenāt super well-seated in the mind.
I was thinking that you could pass individual npins.sources.<source> to _module.args, but Iāll admit I havenāt tried it yet since Iām still only using npins to manage a single nixpkgs instance and just wrote a rebuild.sh shell script ala Pinning NixOS with npins, or how to kill channels forever without flakes - jade's www site for now.
As long as your pins donāt expose modules, this is completely legit, but to @waffle8946ās point, generally you donāt want to use either specialArgs or _modules.args. You want to make new options, which are also exposed globally. Really I just want a modular way to import modules exposed by npins sources, and I donāt think currently there is a way to do that without either this PR, or just writing your own wrapper around ./lib/eval-config.nix, which tbh is trivial, Iād just like to have an upstream solution, because I think itās a good pattern. Ofc, landing depends on if anyone else agrees ![]()
Admittedly my approach of
# some code omitted, see the blog post if you want more
nixpkgs_pin=$(nix-instantiate --eval ./npins -A nixpkgs.outPath |tr -d \")
nix_path="nixpkgs=${nixpkgs_pin}:nixos-config=${PWD}/configuration.nix"
env NIX_PATH="${nix_path}" nixos-rebuild "$cmd" --no-reexec "$@"
is just a long-winded end-run around ./lib/eval-config.nix or, in other words, an indirect way to make a wrapper.
That is why Iām interested in understanding your approach, as it seems better but I know there are parts that arenāt quite there in my mind haha.
Iām sorry, I donāt think I understand that. I thought the whole point of this PR was a way to use specialArgs, but you seem to say above that generally you donāt want to do this. For the second sentence, do you mean that there is a problem in creating an npin-able source that creates new options in the sense of the NixOS module imports/options/config triple without this PR?
I know itās probably a pain to answer; thank you for your generosity in sharing your knowledge.
Itās a good question. What I am doing is something like the following (given default.nix as above, but importing sources using specialArgs).
{ sources, ... }:
{
imports = [ import (sources.lanzaboote).nixosModules.lanzaboote ];
boot = {
lanzaboote = {
enable = true;
pkiBundle = "/var/lib/sbctl";
};
};
}
I think this a good use of specialArgs, because it canāt be written another way (other than slamming it all into one file, or importing npins over and over). I get a nice module that just concerns itself with secure boot. I think a bad use of specialArgs (or _module.args) would be something like specialArgs = { hostname = "hostname" }; so that you could have your hostname injected as module argument. Just expose an option for that (or use networking.hostname).
This is something Iāve been beating my head against too. I appreciate you for sharing this
Oh I hadnāt realized you can import <nixpkgs/nixos> instead of <nixpkgs/nixos/lib/eval-config.nix> (which, of course, lets you set specialArgs)
Yeah, truly this was my tilting at the windmills moment, because it is frivolous. FWIW anyone joining at the end of the thread, this did get merged, and with 25.11 being released I can drop my patches and start offering unsolicited advice on reddit about npins. Truly this is living.
Iām imagining the same kind of thing could be done on nix-darwin and home-manager as well, right? Itād be nice to have this pattern work there too. What could I do to help drive something like that? Iām still catching up with the intricacies of the modules system, but Iām keenly interested in this style of solution because my usecase is the same as yours: providing sources as a parameter to a series of modules. This would naturally directly extend to flakes of course; what Iām ultimately after is interoperability between flake and non-flake interfaces here.
The modules entry point for hm already accepts special args so if you run nh/standalone you already do this with no changes. Iām not holding this up as an example of excellence in code but default.nix Ā· master Ā· Benjamin Edwards / nixnix Ā· GitLab this is my current ātop levelā file. nh home switch -f lamorna.home does the thing for hm. Otherwise youād need to add some extra flags or whatever to home-manager executable and upstream that.
Obviously if you are using the home manager module then this all moot because you are entering via nixos-rebuild which already has the right bits.