From cfaa78937e55ab3d84632cd240efb9c9bd95280e Mon Sep 17 00:00:00 2001 From: Jake Hamilton Date: Sun, 25 Jun 2023 18:58:14 -0700 Subject: [PATCH] fix: rework home-manager modules to allow option merging --- lib/home/default.nix | 123 +++++++++++++++++++++++--------- modules/darwin/user/default.nix | 55 ++++++++------ modules/home/user/default.nix | 6 +- modules/nixos/user/default.nix | 51 +++++++------ 4 files changed, 154 insertions(+), 81 deletions(-) diff --git a/lib/home/default.nix b/lib/home/default.nix index 1526351..035d87b 100644 --- a/lib/home/default.nix +++ b/lib/home/default.nix @@ -1,7 +1,26 @@ { core-inputs, user-inputs, snowfall-lib }: let - inherit (core-inputs.nixpkgs.lib) assertMsg foldl head tail concatMap optionalAttrs mkIf filterAttrs mapAttrs' mkMerge mapAttrsToList optionals mkDefault mkAliasDefinitions; + inherit (core-inputs.nixpkgs.lib) + assertMsg + foldl + head + tail + concatMap + optionalAttrs + optional + mkIf + filterAttrs + mapAttrs' + mkMerge + mapAttrsToList + optionals + mkDefault + traceSeqN + mkAliasDefinitions + mkAliasAndWrapDefinitions + mkOption + types; user-homes-root = snowfall-lib.fs.get-snowfall-file "homes"; user-modules-root = snowfall-lib.fs.get-snowfall-file "modules"; @@ -136,7 +155,11 @@ in src = "${user-modules-root}/home"; }; - user-home-modules-list = builtins.attrValues user-home-modules; + user-home-modules-list = mapAttrsToList + (module-path: module: args@{ pkgs, ... }: (module args) // { + _file = "${user-homes-root}/${module-path}/default.nix"; + }) + user-home-modules; create-home' = home-metadata: let @@ -160,6 +183,30 @@ in create-home-system-modules = users: let created-users = create-homes users; + user-home-modules = snowfall-lib.module.create-modules { + src = "${user-modules-root}/home"; + }; + + shared-modules = mapAttrsToList + (module-path: module: { + _file = "${user-modules-root}/home/${module-path}/default.nix"; + + config = { + home-manager.sharedModules = [ module ]; + }; + }) + user-home-modules; + + snowfall-user-home-module = { + _file = "virtual:snowfallorg/modules/home/user/default.nix"; + + config = { + home-manager.sharedModules = [ + ../../modules/home/user/default.nix + ]; + }; + }; + extra-special-args-module = args@{ config , pkgs @@ -184,15 +231,17 @@ in }; }; }; + system-modules = builtins.map (name: let created-user = created-users.${name}; user-module = head created-user.modules; - other-modules = tail created-user.modules; + other-modules = users.users.${name}.modules or [ ]; user-name = created-user.specialArgs.user; in args@{ config + , options , pkgs , host ? "" , ... @@ -200,52 +249,56 @@ in let host-matches = created-user.specialArgs.host == host; - # @NOTE(jakehamilton): We *must* specify named attributes here in order - # for home-manager to provide them. - wrapped-user-module = home-args@{ pkgs, lib, osConfig ? { }, ... }: - let - user-module-result = import user-module home-args; - user-imports = - if user-module-result ? imports then - user-module-result.imports - else - [ ]; - user-config = - if user-module-result ? config then - user-module-result.config - else - builtins.removeAttrs user-module-result [ "imports" "options" "_file" ]; - user = created-user.specialArgs.user; - in - { - _file = builtins.toString user-module; - imports = user-imports; + # @NOTE(jakehamilton): To conform to the config structure of home-manager, we have to + # remap the options coming from `snowfallorg.user..home.config` since `mkAliasDefinitions` + # does not let us target options within a submodule. + wrap-user-options = user-option: + if (user-option ? "_type") && user-option._type == "merge" then + user-option // { + contents = builtins.map + (merge-entry: + merge-entry.${user-name}.home.config or { } + ) + user-option.contents; + } + else + (builtins.trace '' + ============= + Snowfall Lib: + Option value for `snowfallorg.user.${user-name}` was not detected to be merged. - config = mkMerge [ - user-config - ({ - snowfallorg.user.name = mkDefault user; - }) - (osConfig.snowfallorg.resolved-homes.${user} or { }) - ]; - }; + Please report the issue on GitHub with a link to your configuration so we can debug the problem: + https://github.com/snowfallorg/lib/issues/new + ============= + '') + user-option; in { _file = "virtual:snowfallorg/home/user/${name}"; config = mkIf host-matches { # Initialize user information. - snowfallorg.user.${user-name} = { }; + snowfallorg.user.${user-name}.home.config = { + snowfallorg.user.name = mkDefault user-name; + }; home-manager = { - users.${user-name} = wrapped-user-module; - sharedModules = other-modules; + users.${user-name} = mkAliasAndWrapDefinitions wrap-user-options options.snowfallorg.user; + + # sharedModules = other-modules ++ optional config.snowfallorg.user.${user-name}.home.enable wrapped-user-module; + sharedModules = other-modules ++ optional config.snowfallorg.user.${user-name}.home.enable user-module; }; }; } ) (builtins.attrNames created-users); in - [ extra-special-args-module ] ++ system-modules; + [ + extra-special-args-module + snowfall-user-home-module + ] + ++ (users.modules or [ ]) + ++ shared-modules + ++ system-modules; }; } diff --git a/modules/darwin/user/default.nix b/modules/darwin/user/default.nix index 13046cc..28f0931 100644 --- a/modules/darwin/user/default.nix +++ b/modules/darwin/user/default.nix @@ -1,4 +1,4 @@ -{ pkgs, lib, options, config, ... }: +{ pkgs, lib, options, config, inputs, ... }: let inherit (lib) types mkOption mkDefault foldl optionalAttrs; @@ -17,14 +17,6 @@ let isHidden = mkDefault false; }; }); - - create-resolved-home = resolved-homes: name: - let - user = cfg.user.${name}; - in - resolved-homes // { - ${name} = user.home.config; - }; in { options.snowfallorg = { @@ -40,32 +32,51 @@ in }; home = { + enable = mkOption { + type = types.bool; + default = true; + }; + path = mkOption { type = types.str; default = "/Users/${name}"; }; config = mkOption { - type = types.attrs; - default = { }; + # HM-compatible options taken from: + # https://github.com/nix-community/home-manager/blob/0ee5ab611dc1fbb5180bd7d88d2aeb7841a4d179/nixos/common.nix#L14 + type = types.submoduleWith { + specialArgs = { + osConfig = config; + modulesPath = "${inputs.home-manager}/modules"; + } // config.home-manager.extraSpecialArgs; + modules = [ + ({ lib, modulesPath, ... }: { + imports = import "${modulesPath}/modules.nix" { + inherit pkgs lib; + useNixpkgsModule = !config.home-manager.useGlobalPkgs; + }; + + config = { + submoduleSupport.enable = true; + submoduleSupport.externalPackageInstall = cfg.useUserPackages; + + home.username = config.users.users.${name}.name; + home.homeDirectory = config.users.users.${name}.home; + + nix.package = config.nix.package; + }; + }) + ] ++ config.home-manager.sharedModules; + }; }; }; }; })); }; - - resolved-homes = mkOption { - type = types.attrs; - default = { }; - internal = true; - }; }; config = { - users.users = (foldl (create-system-users) { } (user-names)); - - snowfallorg = { - resolved-homes = (foldl (create-resolved-home) { } (user-names)); - }; + users.users = (foldl create-system-users { } (user-names)); }; } diff --git a/modules/home/user/default.nix b/modules/home/user/default.nix index 3cc9cba..b81fc7e 100644 --- a/modules/home/user/default.nix +++ b/modules/home/user/default.nix @@ -9,6 +9,8 @@ let # when being used in standalone home-manager. To remedy this, we have to refer to the arguments set directly. os-user-home = inputs.osConfig.users.users.${cfg.name}.home or null; + has-user-name = (cfg.user.name or null) != null; + default-home-directory = if (os-user-home != null) then os-user-home @@ -43,8 +45,8 @@ in config = mkIf cfg.user.enable { home = { - username = mkIf (cfg.user.name or null != null) (mkDefault cfg.user.name); - homeDirectory = mkIf (cfg.user.name or null != null) (mkDefault cfg.user.home.directory); + username = mkIf has-user-name (mkDefault cfg.user.name); + homeDirectory = mkIf has-user-name (mkDefault cfg.user.home.directory); }; }; } diff --git a/modules/nixos/user/default.nix b/modules/nixos/user/default.nix index 490a923..a4d12b3 100644 --- a/modules/nixos/user/default.nix +++ b/modules/nixos/user/default.nix @@ -1,4 +1,4 @@ -{ pkgs, lib, options, config, ... }: +{ pkgs, lib, options, config, inputs, ... }: let inherit (lib) types mkOption mkDefault foldl optionalAttrs optional; @@ -15,22 +15,15 @@ let ${name} = { isNormalUser = mkDefault true; - name = mkDefault cfg.name; + name = mkDefault name; home = mkDefault user.home.path; group = mkDefault "users"; - extraGroups = (builtins.trace user.admin) optional user.admin "wheel"; + extraGroups = optional user.admin "wheel"; }; }); - create-resolved-home = resolved-homes: name: - let - user = cfg.user.${name}; - in - resolved-homes // { - ${name} = user.home.config; - }; in { options.snowfallorg = { @@ -58,26 +51,40 @@ in }; config = mkOption { - type = types.attrs; - default = { }; + # HM-compatible options taken from: + # https://github.com/nix-community/home-manager/blob/0ee5ab611dc1fbb5180bd7d88d2aeb7841a4d179/nixos/common.nix#L14 + type = types.submoduleWith { + specialArgs = { + osConfig = config; + modulesPath = "${inputs.home-manager}/modules"; + } // config.home-manager.extraSpecialArgs; + modules = [ + ({ lib, modulesPath, ... }: { + imports = import "${modulesPath}/modules.nix" { + inherit pkgs lib; + useNixpkgsModule = !config.home-manager.useGlobalPkgs; + }; + + config = { + submoduleSupport.enable = true; + submoduleSupport.externalPackageInstall = cfg.useUserPackages; + + home.username = config.users.users.${name}.name; + home.homeDirectory = config.users.users.${name}.home; + + nix.package = config.nix.package; + }; + }) + ] ++ config.home-manager.sharedModules; + }; }; }; }; })); }; - - resolved-homes = mkOption { - type = types.attrs; - default = { }; - internal = true; - }; }; config = { users.users = (foldl (create-system-users) { } (user-names)); - - snowfallorg = { - resolved-homes = (foldl (create-resolved-home) { } (user-names)); - }; }; }