{ pkgs, config, lib, inputs, ... }: # ============================================================================ # User Configuration Module # ============================================================================ # This module defines the schema for user accounts and handles their creation. # It bridges the gap between the data in 'users.nix' and the actual NixOS # and Home Manager configuration. let # Helper: Resolve external module path from fetchGit/fetchTarball/path resolveExternalPath = external: if external == null then null else if builtins.isAttrs external && external ? outPath then external.outPath else external; # Helper: Check if path exists and is valid isValidPath = path: path != null && (builtins.isPath path || (builtins.isString path && lib.hasPrefix "/" path)) && builtins.pathExists path; in { config = { # Generate NixOS users users.users = let enabledAccounts = lib.filterAttrs (_: user: user.enable) config.athenix.users; in lib.mapAttrs ( name: user: let isPlasma6 = config.services.desktopManager.plasma6.enable; defaultPackages = lib.optionals (isPlasma6 && name != "root") [ pkgs.kdePackages.kate ]; finalPackages = lib.subtractLists user.excludePackages (defaultPackages ++ user.extraPackages); shells = { bash = pkgs.bash; zsh = pkgs.zsh; fish = pkgs.fish; tcsh = pkgs.tcsh; }; in rec { isNormalUser = user.isNormalUser; inherit (user) extraGroups hashedPassword; description = if user.description != null then user.description else lib.mkDefault ""; openssh.authorizedKeys.keys = user.opensshKeys; shell = if user.shell != null then shells.${user.shell} else pkgs.bash; packages = finalPackages ++ [ shell ]; } ) enabledAccounts; # Home Manager configs per user home-manager = { useGlobalPkgs = true; useUserPackages = true; extraSpecialArgs = { osConfig = config; inherit inputs; }; users = let enabledAccounts = lib.filterAttrs (_: user: user.enable) config.athenix.users; in lib.mapAttrs ( name: user: let # Resolve external module paths hasExternal = user.external != null; externalPath = resolveExternalPath user.external; userNixPath = if externalPath != null then externalPath + "/user.nix" else null; hasExternalUser = isValidPath userNixPath; # Import external user.nix for home-manager (filter out athenix.* options) externalUserModule = if hasExternalUser then let fullModule = import userNixPath { inherit inputs; }; in # Only pass through non-athenix options to home-manager { config, lib, pkgs, osConfig, ... }: let evaluated = fullModule { inherit config lib pkgs osConfig ; }; in lib.filterAttrs (attrName: _: attrName != "athenix") evaluated else { }; # Common imports based on user flags commonImports = lib.optional user.useZshTheme ../sw/theme.nix ++ [ (import ../sw/nvim.nix { inherit user; }) ]; # Build imports list allImports = user.extraImports ++ commonImports ++ lib.optional hasExternalUser externalUserModule; in lib.mkMerge [ { imports = allImports; # Always set these required options home.username = name; home.homeDirectory = lib.mkOverride 999 (if name == "root" then "/root" else "/home/${name}"); home.stateVersion = "25.11"; programs.${user.editor} = { enable = true; defaultEditor = true; }; } (lib.mkIf (!hasExternal) { # For local users only, add their packages home.packages = user.homePackages; }) ] ) enabledAccounts; }; }; }