From 22aeb0c396820973a7bc6b47d9d9bfd9ec0fc101 Mon Sep 17 00:00:00 2001 From: Gabriel Arazas Date: Thu, 18 Jan 2024 16:54:16 +0800 Subject: [PATCH] flake-parts/setups/nixos: init option to add home-manager users from declarative users setup --- modules/flake-parts/setups/nixos.nix | 253 ++++++++++++++++++++++++--- 1 file changed, 226 insertions(+), 27 deletions(-) diff --git a/modules/flake-parts/setups/nixos.nix b/modules/flake-parts/setups/nixos.nix index 1ce2147e..599f5602 100644 --- a/modules/flake-parts/setups/nixos.nix +++ b/modules/flake-parts/setups/nixos.nix @@ -99,6 +99,49 @@ let }; }; + homeManagerUserType = + let + partsConfig = config; + in + { name, config, lib, ... }: { + options = { + userConfig = lib.mkOption { + type = with lib.types; attrsOf anything; + description = '' + The configuration applied for {option}`users.users.` in the + NixOS configuration. + ''; + }; + + additionalModules = lib.mkOption { + type = with lib.types; listOf raw; + description = '' + A list of additional home-manager modules to be added with the + user. + ''; + }; + }; + + config = + let + hmUserConfig = partsConfig.setups.home-manager.configs.${name}; + in + { + userConfig = { + isNormalUser = lib.mkDefault true; + createHome = lib.mkDefault true; + home = lib.mkForce hmUserConfig.homeDirectory; + }; + + additionalModules = [ + ({ lib, ... }: { + home.homeDirectory = lib.mkForce hmUserConfig.homeDirectory; + home.username = lib.mkForce name; + }) + ]; + }; + }; + configType = let partsConfig = config; @@ -189,6 +232,83 @@ let ''; }; + homeManagerUsers = lib.mkOption { + type = lib.types.submodule { + options = { + users = lib.mkOption { + type = with lib.types; attrsOf (submodule homeManagerUserType); + default = { }; + description = '' + A set of home-manager users from {option}`setups.home-manager` to be + mapped within the NixOS system as a normal user with their + home-manager configuration. This would be the preferred method of + creating NixOS users if you have a more comprehensive home-manager + user that needed more setup to begin with. + ''; + }; + nixpkgsInstance = lib.mkOption { + type = lib.types.enum [ "global" "separate" "none" ]; + default = "global"; + description = '' + Indicates how to manage the nixpkgs instance (or instances) + of the holistic system. This will also dictate how to import + overlays from + {option}`setups.home-manager.configs..overlays`. + + * `global` enforces to use one nixpkgs instance for all + home-manager users and imports all of the overlays into the + nixpkgs instance of the NixOS system. + * `separate` enforces the NixOS system to use individual + nixpkgs instance for all home-manager users and imports the + overlays to the nixpkgs instance of the home-manager user. + * `none` leave the configuration alone and do not import + overlays at all where you have to set them yourself. This is + the best option if you want more control over each individual + NixOS and home-manager configuration. + + The default value is set to `global` which is the encouraged + practice with this module. + ''; + }; + }; + }; + default = { }; + example = lib.literalExpression '' + { + nixpkgsInstance = "global"; + users.foo-dogsquared = { + userConfig = { + extraGroups = [ + "adbusers" + "wheel" + "audio" + "docker" + "podman" + "networkmanager" + "wireshark" + ]; + hashedPassword = + "0000000000000000000000000000000000000000000000"; + isNormalUser = true; + createHome = true; + home = "/home/foo-dogsquared"; + description = "Gabriel Arazas"; + }; + additionalModules = [ + ({ config, lib, osConfig, ... }: { + programs.foo.enable = lib.mkIf osConfig.programs.bar.enable true; + }) + ]; + }; + } + ''; + description = '' + Import home-manager users from + {option}`setups.home-manager.configs` and map them as a normal + NixOS user. + ''; + }; + deploy = lib.mkOption { type = with lib.types; nullOr (submodule deployNodeType); default = null; @@ -206,33 +326,108 @@ let remoteBuild = true; }; }; + }; + + config = { + modules = [ + inputs.${config.homeManagerBranch}.nixosModules.home-manager + ../../../configs/nixos/${name} + + ( + let + setupConfig = config; + hasHomeManagerUsers = config.homeManagerUsers.users != { }; + inherit (config.homeManagerUsers) nixpkgsInstance; + isNixpkgs = state: hasHomeManagerUsers && nixpkgsInstance == state; + in + { config, lib, ... }: { + config = lib.mkMerge [ + { + nixpkgs.overlays = setupConfig.overlays; + networking.hostName = lib.mkDefault setupConfig.hostname; + } + + (lib.mkIf (setupConfig.domain != null) { + networking.domain = lib.mkDefault setupConfig.domain; + }) + + (lib.mkIf hasHomeManagerUsers { + users.users = + lib.mkMerge + (lib.mapAttrsToList + (name: hmUser: { ${name} = hmUser.userConfig; }) + setupConfig.homeManagerUsers.users); + + home-manager.users = lib.mkMerge + (lib.mapAttrsToList + (name: hmUser: { + ${name} = { lib, ... }: { + imports = + partsConfig.setups.home-manager.configs.${name}.modules + ++ hmUser.additionalModules; + }; + }) + setupConfig.homeManagerUsers.users); + }) + + (lib.mkIf (isNixpkgs "global") { + home-manager.useGlobalPkgs = lib.mkForce true; + + # Disable all options that are going to be blocked once + # `home-manager.useGlobalPkgs` is used. + home-manager.users = + lib.mkMerge + (lib.mapAttrsToList + (name: _: { + ${name} = { + nixpkgs.overlays = lib.mkForce null; + nixpkgs.config = lib.mkForce null; + }; + }) + setupConfig.homeManagerUsers.users); + + # Then apply all of the user overlays into the nixpkgs instance + # of the NixOS system. + nixpkgs.overlays = + let + hmUsersOverlays = + lib.mapAttrsToList + (name: _: + partsConfig.setups.home-manager.configs.${name}.overlays) + setupConfig.homeManagerUsers.users; + + overlays = lib.lists.flatten hmUsersOverlays; + in + # Most of the overlays are going to be imported from a flake + # input anyways. This should massively reduce the step needed + # for nixpkgs to do its thing. + # + # Though, it becomes unpredictable due to the way how the + # overlay list is constructed. However, this is much more + # preferable than letting a massive list with duplicated + # overlays from different home-manager users to be applied. + lib.lists.unique overlays; + }) + + (lib.mkIf (isNixpkgs "separate") { + home-manager.useGlobalPkgs = lib.mkForce false; + home-manager.users = + lib.mkMerge + (lib.mapAttrsToList + (name: _: { + ${name} = { + nixpkgs.overlays = + partsConfig.setups.home-manager.configs.${name}.overlays; + }; + }) + setupConfig.homeManagerUsers.users); + }) + ]; + } + ) + ]; + }; }; - - config = { - modules = [ - inputs.${config.homeManagerBranch}.nixosModules.home-manager - ../../../configs/nixos/${name} - - ( - let - setupConfig = config; - in - { config, lib, ... }: { - config = lib.mkMerge [ - { - nixpkgs.overlays = setupConfig.overlays; - networking.hostName = lib.mkDefault setupConfig.hostname; - } - - (lib.mkIf (setupConfig.domain != null) { - networking.domain = lib.mkForce setupConfig.domain; - }) - ]; - } - ) - ]; - }; - }; in { options.setups.nixos = { @@ -309,9 +504,13 @@ in osConfig = config; in { lib, ... }: { + home-manager.sharedModules = osConfig.setups.home-manager.sharedModules; + + # These are just the recommended options for home-manager that may be + # the default value in the future but this is how most of the NixOS + # setups are already done so... home-manager.useUserPackages = lib.mkDefault true; home-manager.useGlobalPkgs = lib.mkDefault true; - home-manager.sharedModules = osConfig.setups.home-manager.sharedModules; } ) ];