{ inputs, hosts ? import ../inventory.nix, ... }: # ============================================================================ # Host Generator # ============================================================================ # This file contains the logic to generate NixOS configurations for all hosts # defined in inventory.nix. It handles: # 1. Common module imports (boot, users, software). # 2. Host-specific overrides (filesystem, enabled users). # 3. External flake integration for system overrides. let nixpkgs = inputs.nixpkgs; lib = nixpkgs.lib; home-manager = inputs.home-manager; disko = inputs.disko; # Modules shared by all hosts commonModules = [ ./boot.nix ./user-config.nix ../users.nix ../sw home-manager.nixosModules.home-manager disko.nixosModules.disko { system.stateVersion = "25.11"; nix.settings.experimental-features = [ "nix-command" "flakes" ]; # Automatic Garbage Collection nix.gc = { automatic = true; dates = "weekly"; options = "--delete-older-than 30d"; }; # Optimize storage nix.optimise.automatic = true; } ]; # Helper to create a single NixOS system configuration mkHost = { hostName, system ? "x86_64-linux", extraModules ? [ ] }: let # Load users.nix to find external user flakes # We use legacyPackages to evaluate the simple data structure of users.nix pkgs = nixpkgs.legacyPackages.${system}; usersData = import ../users.nix { inherit pkgs; }; accounts = usersData.modules.users.accounts or {}; # Extract flakeUrls and convert to modules userFlakeModules = lib.mapAttrsToList (name: user: if (user ? flakeUrl && user.flakeUrl != "") then (builtins.getFlake user.flakeUrl).nixosModules.default else {} ) accounts; in lib.nixosSystem { inherit system; specialArgs = { inherit inputs; }; modules = commonModules ++ userFlakeModules ++ extraModules ++ [ { networking.hostName = hostName; } ]; }; # Function to generate a set of hosts based on inventory count and overrides mkHostGroup = { prefix, count, system ? "x86_64-linux", extraModules ? [], deviceOverrides ? {} }: lib.listToAttrs (map (i: { name = "${prefix}${toString i}"; value = let devConf = deviceOverrides.${toString i} or {}; hasOverride = builtins.hasAttr (toString i) deviceOverrides; # Extract flakeUrl if it exists externalFlake = if hasOverride && (builtins.hasAttr "flakeUrl" devConf) then builtins.getFlake devConf.flakeUrl else null; # Module from external flake externalModule = if externalFlake != null then externalFlake.nixosModules.default else {}; # Config override module (filesystem, users) overrideModule = { ... }: let # Remove special keys that are not filesystem options fsConf = builtins.removeAttrs devConf [ "extraUsers" "flakeUrl" ]; in lib.mkIf hasOverride { host.filesystem = fsConf; modules.users.enabledUsers = devConf.extraUsers or []; }; in mkHost { hostName = "${prefix}${toString i}"; inherit system; extraModules = extraModules ++ [ overrideModule ] ++ (lib.optional (externalFlake != null) externalModule); }; }) (lib.range 1 count)); # Generate host groups based on the input hosts configuration hostGroups = lib.mapAttrsToList (type: config: let typeFile = ./types + "/${type}.nix"; modules = if builtins.pathExists typeFile then import typeFile { inherit inputs; } else throw "Host type '${type}' not found in hosts/types/"; in mkHostGroup { prefix = type; inherit (config) count; extraModules = modules; deviceOverrides = config.devices or {}; } ) hosts; in lib.foldl' lib.recursiveUpdate {} hostGroups