Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Consider adding osConfig to extraSpecialArgs for standalone home manager configurations #69

Open
smkuehnhold opened this issue Feb 4, 2025 · 6 comments
Labels
enhancement New feature or request

Comments

@smkuehnhold
Copy link

smkuehnhold commented Feb 4, 2025

Is your feature request related to a problem? Please describe.

Module-based home manager configurations will have access to an extra extraSpecialArg called osConfig which will alias the hosts' nixosConfiguration/nix-darwin configuration. This option is typically unavailable (set to null) by default in standalone configs however because home manager does not have built-in knowledge of the host system. Since blueprint explicitly maps users to hosts, I see no reason why it couldn't provide this missing functionality to standalone configurations.

It also might cause confusion if a blueprint user creates a new user which depends on osConfig and then later decides to move to standalone home manager. In this case, I think the hm config will not evaluate in standalone due to osConfig being null.

Image

Describe the solution you'd like

Add osConfig to extraSpecialArgs when the hm config is intended to be used in standalone. osConfig should reference the users host config.

Describe alternatives you've considered

You could hard-code each user to depend on flake.outputs.nixosConfigurations.<host>, but that does not seem ideal. No idea if there is a more elegant solution that is already accessible.

Additional context

You might want to consider investigating if there are any other differences in behaviors between module-based and standalone that can be papered over with blueprint

Example config that uses osConfig

{ pkgs, osConfig, ... }:

{
  home.packages = [ pkgs.atool pkgs.httpie ];
  programs.bash.enable = true;

  home.file."test".text = osConfig.system.stateVersion;

  # The state version is required and should stay at the version you
  # originally installed.
  home.stateVersion = "24.11";
}

Module-based build

> nixos-rebuild build --flake .#
building the system configuration...

Standalone build

> home-manager build --flake .
       ...

       … while selecting an attribute
         at /nix/store/hash-source/hosts/host/users/user/home-configuration.nix:7:27:
            6|
            7|   home.file."test".text = osConfig.system.stateVersion;
             |                           ^
            8|

       error: expected a set but found null: null
@smkuehnhold smkuehnhold added the enhancement New feature or request label Feb 4, 2025
@clo4
Copy link
Contributor

clo4 commented Feb 6, 2025

That's kind of cursed, but it's an interesting idea. Seems like a powerful feature that blueprint could expose in a simple way, and the asymmetry you showed between building a system and building its home configuration right now isn't lovely. But it also feels like it could be a footgun for build speed if you reference osConfig, since it's not already evaluated.

Do you have a particular use-case for this in mind? How did this idea pop up? Does this unblock something for you? I want to get an understanding of how this could be used.

I don't have a tonne of time free over the next few days, so feel free to ping me if I've forgotten about this :P

@smkuehnhold
Copy link
Author

smkuehnhold commented Feb 6, 2025

That's kind of cursed, but it's an interesting idea. Seems like a powerful feature that blueprint could expose in a simple way, and the asymmetry you showed between building a system and building its home configuration right now isn't lovely. But it also feels like it could be a footgun for build speed if you reference osConfig, since it's not already evaluated.

Do you have a particular use-case for this in mind? How did this idea pop up? Does this unblock something for you? I want to get an understanding of how this could be used.

I don't have a tonne of time free over the next few days, so feel free to ping me if I've forgotten about this :P

Probably the best use case I have currently is if I need to enable a program/service at the system level, but reference the package at the user level. Since packages are configurable at the nixos-module level, the most correct way to do this is to reference the nixos config itself.

One place I do this in my own setup is with noisetorch. It needs to be enabled at the system level, but I only want some users to run it by default. So I have a hm-module which creates a user service which references programs.noisetorch.package in the nixosConfig.

However none of this really precludes me from moving back to module-based home-manager. It's just personal preference basically. I'm just not really fond of calling sudo anytime I want to add a package to my user environment. And my setups aren't so complicated that eval time has become a concern. So keep that in mind.

@phaer
Copy link
Member

phaer commented Feb 6, 2025

Not yet convinced that passing osConfig would be the best way to handle that tbh.

Couldn't one still use non-standalone home-manager configs in the NixOS config if both are tightly coupled?
standalone home-manager configs can also be used on non-nixos hosts where we might not really have a meaningful osConfig available.

So I have a hm-module which creates a user service which references programs.noisetorch.package in the nixosConfig.

For that alone, flake.outputs.nixosConfigurations.<host> as you suggested above doesn't sound too bad?

@smkuehnhold
Copy link
Author

smkuehnhold commented Feb 6, 2025

Couldn't one still use non-standalone home-manager configs in the NixOS config if both are tightly coupled?

I could, but it's just my preference not to. The ability to edit/activate user configs without dealing with privileges/sudo is a much nicer experience IMO.

For that alone, flake.outputs.nixosConfigurations. as you suggested above doesn't sound too bad?

It's probably not too bad at my scale tbh. More of a convenience. I could see that being annoying though to someone with similar preferences and more hosts.

Also, I understand my use case is probably pretty niche, so I'm not going to push very hard. I'd appreciate however if it (or some similar feature) came around at some point, even if it gave a warning about how it impacts eval times. If you maintainers aren't interested in doing that at this moment, feel free to close this issue.

@clo4
Copy link
Contributor

clo4 commented Feb 9, 2025

It ends up being a rather elegant patch at least!

I still don't know where on the spectrum of ideas this lies, but I've made a test branch you can use to test it out: github:clo4/blueprint/standalone-osconfig (clo4@0b00017)

diff --git a/lib/default.nix b/lib/default.nix
index 5b84b36..e8cf77b 100644
--- a/lib/default.nix
+++ b/lib/default.nix
@@ -215,12 +215,15 @@ let
           mkHomeConfiguration =
             {
               username,
+              hostname,
               modulePath,
               pkgs,
             }:
             home-manager.lib.homeManagerConfiguration {
               inherit pkgs;
-              extraSpecialArgs = specialArgs;
+              extraSpecialArgs = specialArgs // {
+                osConfig = hosts.${hostname}.value.config or { };
+              };
               modules = [
                 perSystemModule
                 modulePath
@@ -261,7 +264,7 @@ let
             homeConfigurations = lib.mapAttrs (
               _name: homeData:
               mkHomeConfiguration {
-                inherit (homeData) modulePath username;
+                inherit (homeData) modulePath username hostname;
                 inherit pkgs;
               }
             ) homesFlat;

Referencing flake.nixosConfigurations.${hostname}.config feels much more explicit, which is arguably better, but it doesn't feel great that the hostname has to be hard-coded since there's no (good) way to get it "dynamically" at the moment.

Could make the hostname a module argument, which would also give users the power to build osConfig themselves.

@smkuehnhold
Copy link
Author

I still don't know where on the spectrum of ideas this lies, but I've made a test branch you can use to test it out: github:clo4/blueprint/standalone-osconfig (clo4@0b00017)

Appreciate you looking into this 🙂, but we have a subtle difference! If the idea was to match homeManager behavior as much as possible, I think what you'd want is hosts.${hostname}.value.config or null rather than hosts.${hostname}.value.config or { }. Otherwise we get this similar but different output on evaluation of a blueprint user without a system configuration.nix (see attribute missing vs null).

error: attribute 'system' missing
 at /nix/store/y8fplrsj6ncqv68p1npk10gm3jjpxah2-source/hosts/smk-desktop/users/smkuehnhold/home-configuration.nix:7:27:
      6|
      7|   home.file."test".text = osConfig.system.stateVersion;
       |                           ^
      8|

Not sure how that would impact downstream, but it definitely makes my original idea seem more dangerous.

Could make the hostname a module argument, which would also give users the power to build osConfig themselves.

This would be more than sufficient for my needs!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants