akavel's digital garden

Passing inputs to Nix modules

Today I learned of basically three patterns for passing flake inputs (and other arguments) into home-manager modules.

The examples bellow assume that you added a new input named foo-bar into your main flake.nix, for example: inputs.foo-bar.url = “…”.

1. Passing packages via overlays

This is the “classic” pattern, harking back to non-flakes NixOS. Assuming that the newly imported foo-bar flake defines a package, you can add it into an overlay like so:

...
 outputs = { self, ... }@inputs: {
   homeConfigurations = {
     "user@host" = let
       system = "aarch64-darwin";
     in inputs.home-manager.lib.homeManagerConfiguration rec {
-      pkgs = inputs.nixpkgs.legacyPackages.${system};
+      pkgs = import inputs.nixpkgs {
+        inherit system;
+        overlays = [
+          (new: old: { foo-bar = inputs.foo-bar.packages.${system}.foo-bar; })
+        ];
+      };
       modules = [
...

Now, a module will have foo-bar in its pkgs.

2. Passing inputs via extraSpecialArgs

This is reportedly based on a similar mechanism in NixOS, called specialArgs. I haven’t tried it yet, but from what I understand, it should allow to pass extra stuff as arguments to modules. The stuff passed can be anything, doesn’t have to be a package. In the main flake.nix, the following needs to be added:

...
 outputs = { self, ... }@inputs: {
   homeConfigurations = {
     "user@host" = let
       system = "aarch64-darwin";
     in inputs.home-manager.lib.homeManagerConfiguration rec {
       pkgs = inputs.nixpkgs.legacyPackages.${system};
+      extraSpecialArgs = { inherit inputs; };  # make inputs available to modules via arg
       modules = [
...

Any other arguments can also be added - the example above adds the whole inputs, it’s basically equivalent to: { extraSpecialArgs = { inputs = inputs; } }.

The change above now makes inputs available as argument to any modules:

-{ pkgs, config, ... }:
+{ pkgs, config, inputs, ... }:
...

3. Passing inputs via _module.args

I also haven’t tried it yet. This pattern seems the newest, and the same in NixOS and home-manager. It’s somewhat similar to variant 2 above (i.e. extraSpecialArgs), but more “local”. I think it may be also “order dependant”, and I’m not sure in what way things get overridden if they’re repeated.

This has the benefit that it allows communication between modules. That’s because it’s defined (partly?) within the structure of the regular “option/config” system.

To pass something into _module.args, you need to define a new module like below:

...
 outputs = { self, ... }@inputs: {
   homeConfigurations = {
     "user@host" = let
       system = "aarch64-darwin";
     in inputs.home-manager.lib.homeManagerConfiguration rec {
       pkgs = inputs.nixpkgs.legacyPackages.${system};
       modules = [
+        { _module.args = { inherit foo-bar; }; }  # make foo-bar available to modules via arg
...

(Note: above is I think a shortcut for { config = { _module.args = { inherit foo-bar; }; }; }).

This should make foo-bar available to (subsequent?) modules, again as a new named argument:

-{ pkgs, config, ... }:
+{ pkgs, config, foo-bar, ... }:
...
🌿 budding — contents of this article got classified among maturing works that I have spent considerable time and energy cultivating but have not finished. They are teenagers who have outgrown their seedling status and may someday grow into ripe.
© Mateusz Czapliński 🐘 Mastodon 🐙 GitHub 🎮 Itch.io ♟️ BGG 🧶 Ravelry