From 5875725ca235fe7d402876d90028e1c50d7b62d1 Mon Sep 17 00:00:00 2001 From: UGA Innovation Factory Date: Wed, 7 Jan 2026 18:11:19 -0500 Subject: [PATCH 01/10] refactor: reorganize directory structure (variants -> hw, glue -> fleet) - Rename variants/ to hw/ for clearer hardware module naming - Rename glue/ to fleet/ for more intuitive fleet management - Move boot/fs configuration from glue/boot.nix to separate fleet/boot.nix and fleet/fs.nix - Improve separation of concerns between boot, filesystem, and common config --- fleet/boot.nix | 48 ++++++++ fleet/common.nix | 51 ++++++++ fleet/fs.nix | 90 ++++++++++++++ fleet/user-config.nix | 276 ++++++++++++++++++++++++++++++++++++++++++ glue/boot.nix | 137 --------------------- glue/common.nix | 99 --------------- hw/default.nix | 28 +++++ hw/nix-desktop.nix | 50 ++++++++ hw/nix-ephemeral.nix | 66 ++++++++++ hw/nix-laptop.nix | 63 ++++++++++ hw/nix-lxc.nix | 61 ++++++++++ hw/nix-surface.nix | 57 +++++++++ hw/nix-wsl.nix | 53 ++++++++ hw/nix-zima.nix | 49 ++++++++ 14 files changed, 892 insertions(+), 236 deletions(-) create mode 100644 fleet/boot.nix create mode 100644 fleet/common.nix create mode 100644 fleet/fs.nix create mode 100644 fleet/user-config.nix delete mode 100644 glue/boot.nix delete mode 100644 glue/common.nix create mode 100644 hw/default.nix create mode 100644 hw/nix-desktop.nix create mode 100644 hw/nix-ephemeral.nix create mode 100644 hw/nix-laptop.nix create mode 100644 hw/nix-lxc.nix create mode 100644 hw/nix-surface.nix create mode 100644 hw/nix-wsl.nix create mode 100644 hw/nix-zima.nix diff --git a/fleet/boot.nix b/fleet/boot.nix new file mode 100644 index 0000000..b2ef67c --- /dev/null +++ b/fleet/boot.nix @@ -0,0 +1,48 @@ +# ============================================================================ +# Boot configuration module +# ============================================================================ +# This module defines: +# - Bootloader configuration (systemd-boot with Plymouth) +# - Timezone and locale settings +# - Systemd sleep configuration + +{ config, lib, ... }: +{ + boot = { + loader.systemd-boot.enable = lib.mkDefault true; + loader.efi.canTouchEfiVariables = lib.mkDefault true; + plymouth.enable = lib.mkDefault true; + + # Enable "Silent boot" + consoleLogLevel = 3; + initrd.verbose = false; + + # Hide the OS choice for bootloaders. + # It's still possible to open the bootloader list by pressing any key + # It will just not appear on screen unless a key is pressed + loader.timeout = lib.mkDefault 0; + }; + + # Set your time zone. + time.timeZone = "America/New_York"; + + # Select internationalisation properties. + i18n.defaultLocale = "en_US.UTF-8"; + + i18n.extraLocaleSettings = { + LC_ADDRESS = "en_US.UTF-8"; + LC_IDENTIFICATION = "en_US.UTF-8"; + LC_MEASUREMENT = "en_US.UTF-8"; + LC_MONETARY = "en_US.UTF-8"; + LC_NAME = "en_US.UTF-8"; + LC_NUMERIC = "en_US.UTF-8"; + LC_PAPER = "en_US.UTF-8"; + LC_TELEPHONE = "en_US.UTF-8"; + LC_TIME = "en_US.UTF-8"; + }; + + systemd.sleep.extraConfig = '' + SuspendState=freeze + HibernateDelaySec=2h + ''; +} diff --git a/fleet/common.nix b/fleet/common.nix new file mode 100644 index 0000000..8bcbb4d --- /dev/null +++ b/fleet/common.nix @@ -0,0 +1,51 @@ +# ============================================================================ +# Common Host Module +# ============================================================================ +# This module contains all the common configuration shared by all host types. +# It is automatically imported by the fleet generator for every host. + +{ + inputs, + config, + lib, + ... +}: +{ + imports = [ + ./fs.nix + ./boot.nix + ./user-config.nix + ../sw + ../users.nix + ]; + + options.athenix = { + forUser = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = null; + description = '' + Convenience option to configure a host for a specific user. + Automatically enables the user (sets athenix.users.username.enable = true). + Value should be a username from athenix.users.accounts. + ''; + }; + host.useHostPrefix = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Whether to prepend the host prefix to the hostname (used in inventory and hosts/default.nix)."; + }; + }; + + config = lib.mkMerge [ + (lib.mkIf (config.athenix.forUser != null) { + athenix.users.${config.athenix.forUser}.enable = true; + }) + { + system.stateVersion = "25.11"; + nix.settings.experimental-features = [ + "nix-command" + "flakes" + ]; + } + ]; +} diff --git a/fleet/fs.nix b/fleet/fs.nix new file mode 100644 index 0000000..97fd6d0 --- /dev/null +++ b/fleet/fs.nix @@ -0,0 +1,90 @@ +# ============================================================================ +# FS & Storage Configuration +# ============================================================================ +# This module defines: +# - Disko partition layout (EFI, swap, root) +# - Filesystem options (device, swap size) + +{ config, lib, ... }: + +{ + options.athenix = { + host.filesystem = { + device = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = null; + description = "The main disk device to use for installation."; + }; + useSwap = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Whether to create and use a swap partition."; + }; + swapSize = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = null; + description = "The size of the swap partition."; + }; + }; + }; + + config = { + # ========== Disk Partitioning (Disko) ========== + disko.enableConfig = lib.mkDefault (config.athenix.host.filesystem.device != null); + + disko.devices = { + disk.main = { + type = "disk"; + device = config.athenix.host.filesystem.device; + content = { + type = "gpt"; + partitions = { + # EFI System Partition + ESP = { + name = "ESP"; + label = "BOOT"; + size = "1G"; + type = "EF00"; + content = { + type = "filesystem"; + format = "vfat"; + mountpoint = "/boot"; + mountOptions = [ "umask=0077" ]; + extraArgs = [ + "-n" + "BOOT" + ]; + }; + }; + + # Swap Partition (size configurable per host) + swap = lib.mkIf config.athenix.host.filesystem.useSwap { + name = "swap"; + label = "swap"; + size = config.athenix.host.filesystem.swapSize; + content = { + type = "swap"; + }; + }; + + # Root Partition (takes remaining space) + root = { + name = "root"; + label = "root"; + size = "100%"; + content = { + type = "filesystem"; + format = "ext4"; + mountpoint = "/"; + extraArgs = [ + "-L" + "ROOT" + ]; + }; + }; + }; + }; + }; + }; + }; +} diff --git a/fleet/user-config.nix b/fleet/user-config.nix new file mode 100644 index 0000000..299acef --- /dev/null +++ b/fleet/user-config.nix @@ -0,0 +1,276 @@ +{ + 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 + # Load users.nix to get account definitions + usersData = import ../users.nix { inherit pkgs; }; + accounts = usersData.athenix.users or { }; + + # 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; + + # Extract athenix.users options from external user.nix modules + # First, build a cache of options per user from their external user.nix (if any). + externalUserModuleOptions = lib.genAttrs (lib.attrNames accounts) ( + name: + let + user = accounts.${name}; + externalPath = resolveExternalPath (user.external or null); + userNixPath = if externalPath != null then externalPath + "/user.nix" else null; + in + if isValidPath userNixPath then + let + # Import and evaluate the module with minimal args + outerModule = import userNixPath { inherit inputs; }; + evaluatedModule = outerModule { + config = { }; + inherit lib pkgs; + osConfig = null; + }; + # Extract just the athenix.users. options + athenixUsers = evaluatedModule.athenix.users or { }; + in + athenixUsers.${name} or { } + else + { } + ); + + # externalUserOptions only contains users that actually have options defined + externalUserOptions = lib.filterAttrs ( + _: moduleOptions: moduleOptions != { } + ) externalUserModuleOptions; + + # Submodule defining the structure of a user account + userSubmodule = lib.types.submodule { + options = { + isNormalUser = lib.mkOption { + type = lib.types.bool; + default = true; + }; + description = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = null; + }; + extraGroups = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = [ ]; + }; + hashedPassword = lib.mkOption { + type = lib.types.str; + default = "!"; + }; + extraPackages = lib.mkOption { + type = lib.types.listOf lib.types.package; + default = [ ]; + }; + excludePackages = lib.mkOption { + type = lib.types.listOf lib.types.package; + default = [ ]; + }; + homePackages = lib.mkOption { + type = lib.types.listOf lib.types.package; + default = [ ]; + }; + extraImports = lib.mkOption { + type = lib.types.listOf lib.types.path; + default = [ ]; + }; + external = lib.mkOption { + type = lib.types.nullOr ( + lib.types.oneOf [ + lib.types.path + lib.types.package + lib.types.attrs + ] + ); + default = null; + description = '' + External user configuration module. Can be: + - A path to a local module directory + - A fetchGit/fetchTarball result pointing to a repository + + The external module can contain: + - user.nix (optional): Sets athenix.users. options AND home-manager config + - nixos.nix (optional): System-level NixOS configuration + + Example: builtins.fetchGit { url = "https://github.com/user/dotfiles"; rev = "..."; } + ''; + }; + opensshKeys = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = [ ]; + description = "List of SSH public keys for the user."; + }; + shell = lib.mkOption { + type = lib.types.nullOr lib.types.package; + default = null; + description = "The shell for this user."; + }; + editor = lib.mkOption { + type = lib.types.nullOr lib.types.package; + default = null; + description = "The default editor for this user."; + }; + useZshTheme = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Whether to apply the system Zsh theme."; + }; + useNvimPlugins = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Whether to apply the system Neovim configuration."; + }; + enable = lib.mkOption { + type = lib.types.bool; + default = false; + description = "Whether this user account is enabled on this system."; + }; + }; + }; +in +{ + + options.athenix.users = lib.mkOption { + type = lib.types.attrsOf userSubmodule; + default = { }; + description = "User accounts configuration. Set enable=true for users that should exist on this system."; + }; + + config = { + # Merge user definitions from users.nix with options from external user.nix modules + # External options take precedence over users.nix (which uses lib.mkDefault) + athenix.users = lib.mapAttrs ( + name: user: + user + // { + description = lib.mkDefault (user.description or null); + shell = lib.mkDefault (user.shell or null); + extraGroups = lib.mkDefault (user.extraGroups or [ ]); + } + // (externalUserOptions.${name} or { }) + ) accounts; + + # 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); + in + { + inherit (user) isNormalUser extraGroups hashedPassword; + description = if user.description != null then user.description else lib.mkDefault ""; + openssh.authorizedKeys.keys = user.opensshKeys; + packages = finalPackages; + shell = if user.shell != null then user.shell else pkgs.bash; + } + ) 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 = if name == "root" then "/root" else "/home/${name}"; + home.stateVersion = "25.11"; + } + (lib.mkIf (!hasExternal) { + # For local users only, add their packages + home.packages = user.homePackages; + }) + ] + ) enabledAccounts; + }; + }; +} diff --git a/glue/boot.nix b/glue/boot.nix deleted file mode 100644 index 8e81434..0000000 --- a/glue/boot.nix +++ /dev/null @@ -1,137 +0,0 @@ -# ============================================================================ -# Boot & Storage Configuration -# ============================================================================ -# This module defines: -# - Disko partition layout (EFI, swap, root) -# - Bootloader configuration (systemd-boot with Plymouth) -# - Filesystem options (device, swap size) -# - Build method options (used by installer/artifacts.nix) -# - Convenience options (forUser, useHostPrefix) - -{ config, lib, ... }: - -{ - options.athenix = { - host = { - useHostPrefix = lib.mkOption { - type = lib.types.bool; - default = true; - description = "Whether to prepend the host prefix to the hostname (used in inventory and hosts/default.nix)."; - }; - filesystem = { - device = lib.mkOption { - type = lib.types.str; - description = "The main disk device to use for installation."; - }; - useSwap = lib.mkOption { - type = lib.types.bool; - default = true; - description = "Whether to create and use a swap partition."; - }; - swapSize = lib.mkOption { - type = lib.types.str; - description = "The size of the swap partition."; - }; - }; - }; - }; - - config = { - # ========== Disk Partitioning (Disko) ========== - disko.enableConfig = lib.mkDefault true; - - disko.devices = { - disk.main = { - type = "disk"; - device = config.athenix.host.filesystem.device; - content = { - type = "gpt"; - partitions = { - # EFI System Partition - ESP = { - name = "ESP"; - label = "BOOT"; - size = "1G"; - type = "EF00"; - content = { - type = "filesystem"; - format = "vfat"; - mountpoint = "/boot"; - mountOptions = [ "umask=0077" ]; - extraArgs = [ - "-n" - "BOOT" - ]; - }; - }; - - # Swap Partition (size configurable per host) - swap = lib.mkIf config.athenix.host.filesystem.useSwap { - name = "swap"; - label = "swap"; - size = config.athenix.host.filesystem.swapSize; - content = { - type = "swap"; - }; - }; - - # Root Partition (takes remaining space) - root = { - name = "root"; - label = "root"; - size = "100%"; - content = { - type = "filesystem"; - format = "ext4"; - mountpoint = "/"; - extraArgs = [ - "-L" - "ROOT" - ]; - }; - }; - }; - }; - }; - }; - - # Bootloader Configuration - boot = { - loader.systemd-boot.enable = true; - loader.efi.canTouchEfiVariables = true; - plymouth.enable = true; - - # Enable "Silent boot" - consoleLogLevel = 3; - initrd.verbose = false; - - # Hide the OS choice for bootloaders. - # It's still possible to open the bootloader list by pressing any key - # It will just not appear on screen unless a key is pressed - loader.timeout = lib.mkDefault 0; - }; - - # Set your time zone. - time.timeZone = "America/New_York"; - - # Select internationalisation properties. - i18n.defaultLocale = "en_US.UTF-8"; - - i18n.extraLocaleSettings = { - LC_ADDRESS = "en_US.UTF-8"; - LC_IDENTIFICATION = "en_US.UTF-8"; - LC_MEASUREMENT = "en_US.UTF-8"; - LC_MONETARY = "en_US.UTF-8"; - LC_NAME = "en_US.UTF-8"; - LC_NUMERIC = "en_US.UTF-8"; - LC_PAPER = "en_US.UTF-8"; - LC_TELEPHONE = "en_US.UTF-8"; - LC_TIME = "en_US.UTF-8"; - }; - - systemd.sleep.extraConfig = '' - SuspendState=freeze - HibernateDelaySec=2h - ''; - }; -} diff --git a/glue/common.nix b/glue/common.nix deleted file mode 100644 index c7ad4ae..0000000 --- a/glue/common.nix +++ /dev/null @@ -1,99 +0,0 @@ -# ============================================================================ -# Common Host Module -# ============================================================================ -# This module contains all the common configuration shared by all host types. -# It is automatically imported by the fleet generator for every host. - -{ inputs }: -{ - config, - lib, - ... -}: -{ - imports = [ - ./boot.nix - ./user-config.nix - ../sw - ../users.nix - inputs.home-manager.nixosModules.home-manager - inputs.agenix.nixosModules.default - inputs.disko.nixosModules.disko - ]; - - # Define garbage collection options here since they're consumed in this module - options.athenix = { - forUser = lib.mkOption { - type = lib.types.nullOr lib.types.str; - default = null; - description = '' - Convenience option to configure a host for a specific user. - Automatically enables the user (sets athenix.users.username.enable = true). - Value should be a username from athenix.users.accounts. - ''; - }; - - system.gc = { - enable = lib.mkOption { - type = lib.types.bool; - default = true; - description = "Whether to enable automatic garbage collection."; - }; - frequency = lib.mkOption { - type = lib.types.str; - default = "weekly"; - description = "How often to run garbage collection (systemd timer format)."; - }; - retentionDays = lib.mkOption { - type = lib.types.int; - default = 30; - description = "Number of days to keep old generations before deletion."; - }; - optimise = lib.mkOption { - type = lib.types.bool; - default = true; - description = "Whether to automatically optimize the Nix store."; - }; - }; - - host.buildMethods = lib.mkOption { - type = lib.types.listOf lib.types.str; - default = [ "installer-iso" ]; - description = '' - List of allowed build methods for this host (used by installer/artifacts.nix). - Supported methods: - - "installer-iso": Generates an auto-install ISO that installs this configuration to disk. - - "iso": Generates a live ISO (using nixos-generators). - - "ipxe": Generates iPXE netboot artifacts (kernel, initrd, script). - - "lxc": Generates an LXC container tarball. - - "proxmox": Generates a Proxmox VMA archive. - ''; - }; - }; - - config = lib.mkMerge [ - # Enable forUser if specified - (lib.mkIf (config.athenix.forUser != null) { - athenix.users.${config.athenix.forUser}.enable = true; - }) - - { - system.stateVersion = "25.11"; - - nix.settings.experimental-features = [ - "nix-command" - "flakes" - ]; - - # Automatic Garbage Collection - nix.gc = lib.mkIf config.athenix.system.gc.enable { - automatic = true; - dates = config.athenix.system.gc.frequency; - options = "--delete-older-than ${toString config.athenix.system.gc.retentionDays}d"; - }; - - # Optimize storage - nix.optimise.automatic = config.athenix.system.gc.optimise; - } - ]; -} diff --git a/hw/default.nix b/hw/default.nix new file mode 100644 index 0000000..0b95ad2 --- /dev/null +++ b/hw/default.nix @@ -0,0 +1,28 @@ +# ============================================================================ +# Host Types Module +# ============================================================================ +# This module exports all available host types as an attribute set. +# Each type is a NixOS module (a function suitable for lib.types.submodule). + +{ inputs }: +let + lib = inputs.nixpkgs.lib; + inherit (builtins) readDir attrNames; + inherit (lib) filterAttrs removeSuffix genAttrs; + + files = readDir ./.; + + # Keep only regular *.nix files except default.nix + nixFiles = filterAttrs ( + name: type: + type == "regular" + && lib.hasSuffix ".nix" name + && name != "default.nix" + ) files; + + moduleNames = map (name: removeSuffix ".nix" name) (attrNames nixFiles); +in +# Export: { name = ; } +genAttrs moduleNames (name: + import (./. + ("/" + name + ".nix")) +) \ No newline at end of file diff --git a/hw/nix-desktop.nix b/hw/nix-desktop.nix new file mode 100644 index 0000000..d7116f9 --- /dev/null +++ b/hw/nix-desktop.nix @@ -0,0 +1,50 @@ +# ============================================================================ +# Desktop Configuration +# ============================================================================ +# Hardware and boot configuration for standard desktop workstations. +# Includes Intel CPU support and NVMe storage. + +{ + config, + lib, + modulesPath, + ... +}: +{ + imports = [ + (modulesPath + "/installer/scan/not-detected.nix") + ]; + + # ========== Boot Configuration ========== + + boot.initrd.availableKernelModules = [ + "xhci_pci" # USB 3.0 support + "nvme" # NVMe SSD support + "usb_storage" # USB storage devices + "sd_mod" # SD card support + "sdhci_pci" # SD card host controller + ]; + boot.initrd.kernelModules = [ ]; + boot.kernelModules = [ "kvm-intel" ]; # Intel virtualization support + boot.extraModulePackages = [ ]; + boot.kernelParams = [ + "quiet" # Minimal boot messages + "splash" # Show Plymouth boot splash + "boot.shell_on_fail" # Emergency shell on boot failure + "udev.log_priority=3" # Reduce udev logging + "rd.systemd.show_status=auto" # Show systemd status during boot + ]; + + # ========== Filesystem Configuration ========== + athenix.host.filesystem.swapSize = lib.mkDefault "16G"; + athenix.host.filesystem.device = lib.mkDefault "/dev/nvme0n1"; + athenix.host.buildMethods = lib.mkDefault [ "installer-iso" ]; + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; + + # ========== Hardware Configuration ========== + hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; + + # ========== Software Profile ========== + athenix.sw.enable = lib.mkDefault true; + athenix.sw.type = lib.mkDefault "desktop"; +} diff --git a/hw/nix-ephemeral.nix b/hw/nix-ephemeral.nix new file mode 100644 index 0000000..3d71a81 --- /dev/null +++ b/hw/nix-ephemeral.nix @@ -0,0 +1,66 @@ +# ============================================================================ +# Ephemeral/Diskless System Configuration +# ============================================================================ +# Configuration for systems that run entirely from RAM without persistent storage. +# Suitable for kiosks, netboot clients, and stateless workstations. +# All data is lost on reboot. + +{ + config, + lib, + modulesPath, + ... +}: +{ + imports = [ + (modulesPath + "/installer/scan/not-detected.nix") + ]; + + # ========== Boot Configuration ========== + boot.initrd.availableKernelModules = [ + "xhci_pci" # USB 3.0 support + "nvme" # NVMe support + "usb_storage" # USB storage devices + "sd_mod" # SD card support + "sdhci_pci" # SD card host controller + ]; + boot.initrd.kernelModules = [ ]; + boot.kernelModules = [ "kvm-intel" ]; # Intel virtualization support + boot.extraModulePackages = [ ]; + boot.kernelParams = [ + "quiet" # Minimal boot messages + "splash" # Show Plymouth boot splash + "boot.shell_on_fail" # Emergency shell on boot failure + "udev.log_priority=3" # Reduce udev logging + "rd.systemd.show_status=auto" # Show systemd status during boot + ]; + + # ========== Ephemeral Configuration ========== + # No persistent storage - everything runs from RAM + athenix.host.filesystem.swapSize = lib.mkForce "0G"; + athenix.host.filesystem.device = lib.mkForce "/dev/null"; # Dummy device + athenix.host.buildMethods = lib.mkDefault [ + "iso" # Live ISO image + "ipxe" # Network boot + ]; + + # Disable disk management for RAM-only systems + disko.enableConfig = lib.mkForce false; + + # Define tmpfs root filesystem + fileSystems."/" = { + device = "none"; + fsType = "tmpfs"; + options = [ + "defaults" + "size=50%" + "mode=755" + ]; + }; + + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; + hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; + + athenix.sw.enable = lib.mkDefault true; + athenix.sw.type = lib.mkDefault "stateless-kiosk"; +} diff --git a/hw/nix-laptop.nix b/hw/nix-laptop.nix new file mode 100644 index 0000000..ec0e60e --- /dev/null +++ b/hw/nix-laptop.nix @@ -0,0 +1,63 @@ +# ============================================================================ +# Laptop Configuration +# ============================================================================ +# Hardware and boot configuration for laptop systems with mobile features. +# Includes power management, lid switch handling, and Intel graphics fixes. + +{ + config, + lib, + modulesPath, + ... +}: +{ + imports = [ + (modulesPath + "/installer/scan/not-detected.nix") + ]; + + # ========== Boot Configuration ========== + + boot.initrd.availableKernelModules = [ + "xhci_pci" # USB 3.0 support + "thunderbolt" # Thunderbolt support + "nvme" # NVMe SSD support + "usb_storage" # USB storage devices + "sd_mod" # SD card support + "sdhci_pci" # SD card host controller + ]; + boot.initrd.kernelModules = [ ]; + boot.kernelModules = [ "kvm-intel" ]; # Intel virtualization support + boot.extraModulePackages = [ ]; + boot.kernelParams = [ + "quiet" # Minimal boot messages + "splash" # Show Plymouth boot splash + "boot.shell_on_fail" # Emergency shell on boot failure + "udev.log_priority=3" # Reduce udev logging + "rd.systemd.show_status=auto" # Show systemd status during boot + "i915.enable_psr=0" # Disable Panel Self Refresh (stability) + "i915.enable_dc=0" # Disable display power saving + "i915.enable_fbc=0" # Disable framebuffer compression + ]; + + # ========== Hardware Configuration ========== + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; + hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; + + # ========== Filesystem Configuration ========== + athenix.host.filesystem.device = lib.mkDefault "/dev/nvme0n1"; + athenix.host.filesystem.swapSize = lib.mkDefault "34G"; # Larger swap for hibernation + athenix.host.buildMethods = lib.mkDefault [ "installer-iso" ]; + + # ========== Power Management ========== + services.upower.enable = lib.mkDefault true; + services.logind.settings = { + Login = { + HandleLidSwitch = "suspend"; + HandleLidSwitchExternalPower = "suspend"; + HandleLidSwitchDocked = "ignore"; + }; + }; + + athenix.sw.enable = lib.mkDefault true; + athenix.sw.type = lib.mkDefault "desktop"; +} diff --git a/hw/nix-lxc.nix b/hw/nix-lxc.nix new file mode 100644 index 0000000..a5b161d --- /dev/null +++ b/hw/nix-lxc.nix @@ -0,0 +1,61 @@ +# ============================================================================ +# Proxmox LXC Container Configuration +# ============================================================================ +# Configuration for lightweight Linux containers running in Proxmox. +# Disables boot/disk management and enables remote development support. + +{ + config, + lib, + modulesPath, + inputs, + ... +}: +{ + imports = [ + inputs.vscode-server.nixosModules.default + "${modulesPath}/virtualisation/proxmox-lxc.nix" + ]; + + # ========== Nix Configuration ========== + nix.settings.trusted-users = [ + "root" + "engr-ugaif" + ]; + nix.settings.experimental-features = [ + "nix-command" + "flakes" + ]; + + # ========== Container-Specific Configuration ========== + boot.isContainer = true; + boot.loader.systemd-boot.enable = lib.mkForce false; # No bootloader in container + disko.enableConfig = lib.mkForce false; # No disk management in container + console.enable = true; + + # Allow getty to work in containers + systemd.services."getty@".unitConfig.ConditionPathExists = [ + "" + "/dev/%I" + ]; + + # Suppress unnecessary systemd units for containers + systemd.suppressedSystemUnits = [ + "dev-mqueue.mount" + "sys-kernel-debug.mount" + "sys-fs-fuse-connections.mount" + ]; + + # ========== Remote Development ========== + services.vscode-server.enable = true; + + # ========== System Configuration ========== + system.stateVersion = "25.11"; + athenix.host.buildMethods = lib.mkDefault [ + "lxc" # LXC container tarball + "proxmox" # Proxmox VMA archive + ]; + + athenix.sw.enable = lib.mkDefault true; + athenix.sw.type = lib.mkDefault "headless"; +} diff --git a/hw/nix-surface.nix b/hw/nix-surface.nix new file mode 100644 index 0000000..4452f88 --- /dev/null +++ b/hw/nix-surface.nix @@ -0,0 +1,57 @@ +# ============================================================================ +# Microsoft Surface Tablet Configuration +# ============================================================================ +# Hardware configuration for Surface Go tablets in kiosk mode. +# Uses nixos-hardware module and older kernel for Surface-specific drivers. + +{ + config, + lib, + modulesPath, + inputs, + ... +}: +{ + imports = [ + (modulesPath + "/installer/scan/not-detected.nix") + inputs.nixos-hardware.nixosModules.microsoft-surface-go + ]; + + # ========== Boot Configuration ========== + + boot.initrd.availableKernelModules = [ + "xhci_pci" # USB 3.0 support + "nvme" # NVMe support (though Surface uses eMMC) + "usb_storage" # USB storage devices + "sd_mod" # SD card support + "sdhci_pci" # SD card host controller + ]; + boot.initrd.kernelModules = [ ]; + boot.kernelModules = [ "kvm-intel" ]; # Intel virtualization support + boot.extraModulePackages = [ ]; + boot.kernelParams = [ + "quiet" # Minimal boot messages + "splash" # Show Plymouth boot splash + "boot.shell_on_fail" # Emergency shell on boot failure + "udev.log_priority=3" # Reduce udev logging + "rd.systemd.show_status=auto" # Show systemd status during boot + "intel_ipu3_imgu" # Intel camera image processing + "intel_ipu3_isys" # Intel camera sensor interface + "fbcon=map:1" # Framebuffer console mapping + "i915.enable_psr=0" # Disable Panel Self Refresh (breaks resume) + "i915.enable_dc=0" # Disable display power saving + ]; + + # ========== Filesystem Configuration ========== + athenix.host.filesystem.swapSize = lib.mkDefault "8G"; + athenix.host.filesystem.device = lib.mkDefault "/dev/mmcblk0"; # eMMC storage # eMMC storage + athenix.host.buildMethods = lib.mkDefault [ "installer-iso" ]; + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; + + # ========== Hardware Configuration ========== + hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; + + # ========== Software Profile ========== + athenix.sw.enable = lib.mkDefault true; + athenix.sw.type = lib.mkDefault "tablet-kiosk"; # Touch-optimized kiosk mode +} diff --git a/hw/nix-wsl.nix b/hw/nix-wsl.nix new file mode 100644 index 0000000..6e9e6e4 --- /dev/null +++ b/hw/nix-wsl.nix @@ -0,0 +1,53 @@ +# ============================================================================ +# Windows Subsystem for Linux (WSL) Configuration +# ============================================================================ +# Configuration for NixOS running in WSL2 on Windows. +# Integrates with nixos-wsl for WSL-specific functionality. + +{ + lib, + config, + inputs, + ... +}: +{ + imports = [ + inputs.nixos-wsl.nixosModules.default + inputs.vscode-server.nixosModules.default + ]; + + # ========== Options ========== + options.athenix.host.wsl.user = lib.mkOption { + type = lib.types.str; + default = "engr-ugaif"; + description = "The default user to log in as in WSL."; + }; + + config = { + # ========== WSL Configuration ========== + wsl.enable = true; + # Use forUser if set, otherwise fall back to wsl.user option + wsl.defaultUser = + if config.athenix.forUser != null then config.athenix.forUser else config.athenix.host.wsl.user; + + # ========== Software Profile ========== + athenix.sw.enable = lib.mkDefault true; + athenix.sw.type = lib.mkDefault "headless"; + + # ========== Remote Development ========== + services.vscode-server.enable = true; + + # ========== Disable Irrelevant Systems ========== + # WSL doesn't use traditional boot or disk management + disko.enableConfig = lib.mkForce false; + boot.loader.systemd-boot.enable = lib.mkForce false; + boot.loader.grub.enable = lib.mkForce false; + + # WSL manages its own networking + systemd.network.enable = lib.mkForce false; + + # Provide dummy values for required options from boot.nix + athenix.host.filesystem.device = "/dev/null"; + athenix.host.filesystem.swapSize = "0G"; + }; +} diff --git a/hw/nix-zima.nix b/hw/nix-zima.nix new file mode 100644 index 0000000..a87ef60 --- /dev/null +++ b/hw/nix-zima.nix @@ -0,0 +1,49 @@ +# ============================================================================ +# Desktop Configuration +# ============================================================================ +# Hardware and boot configuration for standard desktop workstations. +# Includes Intel CPU support and NVMe storage. + +{ + config, + lib, + modulesPath, + ... +}: +{ + imports = [ + (modulesPath + "/installer/scan/not-detected.nix") + ]; + + # ========== Boot Configuration ========== + + boot.initrd.availableKernelModules = [ + "xhci_pci" # USB 3.0 support + "usb_storage" # USB storage devices + "sd_mod" # SD card support + "sdhci_pci" # SD card host controller + ]; + boot.initrd.kernelModules = [ ]; + boot.kernelModules = [ "kvm-intel" ]; # Intel virtualization support + boot.extraModulePackages = [ ]; + boot.kernelParams = [ + "quiet" # Minimal boot messages + "splash" # Show Plymouth boot splash + "boot.shell_on_fail" # Emergency shell on boot failure + "udev.log_priority=3" # Reduce udev logging + "rd.systemd.show_status=auto" # Show systemd status during boot + ]; + + # ========== Filesystem Configuration ========== + athenix.host.filesystem.useSwap = lib.mkDefault false; + athenix.host.filesystem.device = lib.mkDefault "/dev/mmcblk0"; + athenix.host.buildMethods = lib.mkDefault [ "installer-iso" ]; + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; + + # ========== Hardware Configuration ========== + hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; + + # ========== Software Profile ========== + athenix.sw.enable = lib.mkDefault true; + athenix.sw.type = lib.mkDefault "desktop"; +} -- 2.39.5 From d15b4d8067fb818e136c1d08bf43f56d65c28c64 Mon Sep 17 00:00:00 2001 From: UGA Innovation Factory Date: Wed, 7 Jan 2026 18:11:29 -0500 Subject: [PATCH 02/10] feat: add fleet configuration options module - Create fleet/fleet-option.nix to define athenix.fleet and athenix.hwTypes options - Use lib.evalModules to evaluate inventory separately from host configs - Enable external overrides of fleet and hardware types via lib.mkForce - Fix infinite recursion issue by avoiding config references in imports --- fleet/default.nix | 292 +++++++++++++++++++++++++++++++++++++++++ fleet/fleet-option.nix | 66 ++++++++++ 2 files changed, 358 insertions(+) create mode 100644 fleet/default.nix create mode 100644 fleet/fleet-option.nix diff --git a/fleet/default.nix b/fleet/default.nix new file mode 100644 index 0000000..16e1b7b --- /dev/null +++ b/fleet/default.nix @@ -0,0 +1,292 @@ +{ + inputs, + fleet ? null, + hwTypes ? null, + ... +}: + +# ============================================================================ +# Fleet Generator +# ============================================================================ +# This file contains the logic to generate NixOS configurations for all hosts +# defined in inventory.nix. It supports both hostname-based and count-based +# configurations with flexible type associations. + +let + nixpkgs = inputs.nixpkgs; + lib = nixpkgs.lib; + + # Evaluate inventory to get fleet data + # Import fleet-option.nix (defines athenix.fleet) and inventory.nix (sets values) + # We use a minimal module here to avoid circular dependencies from common.nix's imports + inventoryModule = lib.evalModules { + modules = [ + (import ./fleet-option.nix { inherit inputs; }) + { _module.args = { pkgs = nixpkgs.legacyPackages.x86_64-linux; }; } + (lib.mkIf (fleet != null) { athenix.fleet = lib.mkForce fleet; }) + (lib.mkIf (hwTypes != null) { athenix.hwTypes = lib.mkForce hwTypes; }) + ]; + }; + + hostTypes = inventoryModule.config.athenix.hwTypes; + + # Helper to create a single NixOS system configuration + mkHost = + { + hostName, + system ? "x86_64-linux", + hostType, + configOverrides ? { }, + externalModuleThunk ? null, + }: + let + # Lazy evaluation: only fetch external module when building this host + externalModulePath = + if externalModuleThunk != null then + let + # Force evaluation of the thunk (fetchGit, fetchTarball, etc.) + fetchedPath = externalModuleThunk; + # Extract outPath from fetchGit/fetchTarball results + extractedPath = + if builtins.isAttrs fetchedPath && fetchedPath ? outPath then fetchedPath.outPath else fetchedPath; + in + if builtins.isPath extractedPath then + extractedPath + "/default.nix" + else if lib.isDerivation extractedPath then + extractedPath + "/default.nix" + else + extractedPath + "/default.nix" + else + null; + + # Load users.nix to find external user modules + pkgs = nixpkgs.legacyPackages.${system}; + usersData = import ../users.nix { inherit pkgs; }; + accounts = usersData.athenix.users or { }; + + # Build a map of user names to their nixos module paths (if they exist) + # We'll use this to conditionally import modules based on user.enable + userNixosModulePaths = lib.filterAttrs (_: v: v != null) ( + lib.mapAttrs ( + name: user: + if (user ? external && user.external != null) then + let + externalPath = + if builtins.isAttrs user.external && user.external ? outPath then + user.external.outPath + else + user.external; + nixosModulePath = externalPath + "/nixos.nix"; + in + if + (builtins.isPath externalPath || (builtins.isString externalPath && lib.hasPrefix "/" externalPath)) + && builtins.pathExists nixosModulePath + then + nixosModulePath + else + null + else + null + ) accounts + ); + + # Create conditional wrapper modules for each user's nixos.nix + # Each wrapper checks if the user is enabled before applying the module content + userNixosModules = lib.mapAttrsToList ( + name: modulePath: + { + config, + lib, + pkgs, + ... + }@args: + let + # Import the user's nixos module - it returns a function or attrset + importedModuleFunc = import modulePath { inherit inputs; }; + # If it's a function, call it with the module args; otherwise use as-is + importedModule = + if lib.isFunction importedModuleFunc then importedModuleFunc args else importedModuleFunc; + in + { + config = lib.mkIf (config.athenix.users.${name}.enable or false) importedModule; + } + ) userNixosModulePaths; + + # Get the host type module from the hostTypes attribute set + typeModule = + hostTypes.${hostType} + or (throw "Host type '${hostType}' not found. Available types: ${lib.concatStringsSep ", " (lib.attrNames hostTypes)}"); + + # External module from fetchGit/fetchurl + externalPathModule = + if externalModulePath != null then import externalModulePath { inherit inputs; } else { }; + + # Config override module - translate special keys to athenix options + overrideModule = + { ... }: + let + cleanConfig = lib.removeAttrs configOverrides [ + "type" + "count" + "devices" + "overrides" + "defaultCount" + "buildMethods" + ]; + specialConfig = lib.optionalAttrs (configOverrides ? buildMethods) { + athenix.host.buildMethods = configOverrides.buildMethods; + }; + in + { + config = lib.mkMerge [ + cleanConfig + specialConfig + ]; + }; + + allModules = + userNixosModules + ++ [ + ./common.nix + typeModule + overrideModule + { networking.hostName = hostName; } + ] + ++ lib.optional (externalModulePath != null) externalPathModule; + in + { + system = lib.nixosSystem { + inherit system; + specialArgs = { inherit inputs; }; + modules = allModules; + }; + modules = allModules; + }; + + # Process inventory entries - top-level keys are always prefixes + processInventory = lib.mapAttrs ( + prefix: config: + let + hostType = config.type or prefix; + system = config.system or "x86_64-linux"; + devices = config.devices or { }; + hasCount = config ? count; + + # Helper to generate hostname from prefix and suffix + # Numbers get no dash: "nix-surface1", "nix-surface2" + # Letters get dash: "nix-surface-alpha", "nix-surface-beta" + mkHostName = + prefix: suffix: usePrefix: + if !usePrefix then + suffix + else if lib.match "[0-9]+" suffix != null then + "${prefix}${suffix}" # numeric: no dash + else + "${prefix}-${suffix}"; # non-numeric: add dash + + # Extract common overrides and default count + overrides = config.overrides or { }; + defaultCount = config.defaultCount or 0; + + # If devices is a number, treat it as count + devicesValue = config.devices or { }; + actualDevices = if lib.isInt devicesValue then { } else devicesValue; + actualCount = if lib.isInt devicesValue then devicesValue else (config.count or 0); + + # Clean base config - remove inventory management keys + baseConfig = lib.removeAttrs config [ + "type" + "system" + "count" + "devices" + "overrides" + "defaultCount" + ]; + + # Generate hosts from explicit device definitions + deviceHosts = lib.listToAttrs ( + lib.mapAttrsToList ( + deviceKey: deviceConfig: + let + # Check if deviceConfig has an 'external' field for lazy evaluation + hasExternalField = builtins.isAttrs deviceConfig && deviceConfig ? external; + + # Extract external module thunk if present (don't evaluate yet!) + externalModuleThunk = if hasExternalField then deviceConfig.external else null; + + # Remove 'external' from config to avoid conflicts + cleanDeviceConfig = + if hasExternalField then lib.removeAttrs deviceConfig [ "external" ] else deviceConfig; + + # Merge: base config -> overrides -> device-specific config + mergedConfig = lib.recursiveUpdate (lib.recursiveUpdate baseConfig overrides) cleanDeviceConfig; + + # Check useHostPrefix from the merged config + usePrefix = mergedConfig.athenix.host.useHostPrefix or true; + hostName = mkHostName prefix deviceKey usePrefix; + in + { + name = hostName; + value = mkHost { + inherit + hostName + system + hostType + externalModuleThunk + ; + configOverrides = mergedConfig; + }; + } + ) actualDevices + ); + + # Generate numbered hosts from count or defaultCount + # If devices are specified, defaultCount fills in the gaps + countToUse = if actualCount > 0 then actualCount else defaultCount; + + # Get which numbered keys are already defined in devices + existingNumbers = lib.filter (k: lib.match "[0-9]+" k != null) (lib.attrNames actualDevices); + + countHosts = + if countToUse > 0 then + lib.listToAttrs ( + lib.filter (x: x != null) ( + map ( + i: + let + deviceKey = toString i; + # Skip if this number is already in explicit devices + alreadyDefined = lib.elem deviceKey existingNumbers; + in + if alreadyDefined then + null + else + let + hostName = mkHostName prefix deviceKey true; + mergedConfig = lib.recursiveUpdate baseConfig overrides; + in + { + name = hostName; + value = mkHost { + inherit hostName system hostType; + configOverrides = mergedConfig; + }; + } + ) (lib.range 1 countToUse) + ) + ) + else + { }; + in + lib.recursiveUpdate deviceHosts countHosts + ); + + fleetData = inventoryModule.config.athenix.fleet; + + # Flatten the nested structure + allHosts = lib.foldl' lib.recursiveUpdate { } (lib.attrValues (processInventory fleetData)); +in +{ + nixosConfigurations = lib.mapAttrs (n: v: v.system) allHosts; + modules = lib.mapAttrs (n: v: v.modules) allHosts; +} diff --git a/fleet/fleet-option.nix b/fleet/fleet-option.nix new file mode 100644 index 0000000..b56a99f --- /dev/null +++ b/fleet/fleet-option.nix @@ -0,0 +1,66 @@ +# ============================================================================ +# Fleet Option Definition +# ============================================================================ +# This module only defines the athenix.fleet option without any dependencies. +# Used by fleet.nix to evaluate inventory data without circular dependencies. +{ inputs, ... }: +{ lib, ... }: +let + fleetDefinition = lib.mkOption { + description = "Hardware types definitions for the fleet."; + type = lib.types.attrsOf (lib.types.submodule ({ name, ...}: { + options = { + type = lib.mkOption { + type = lib.types.oneOf [ + lib.types.str + lib.types.listOf lib.types.str + ]; + default = name; + description = "Type(s) of system configuration for this device."; + }; + system = lib.mkOption { + type = lib.types.str; + default = "x86_64-linux"; + description = "NixOS system architecture for this hardware type."; + }; + devices = lib.mkOption { + type = lib.types.oneOf [ + lib.types.int + (lib.types.attrsOf + (lib.types.submodule ({ name, ... }: { + freeformType = lib.types.attrs; + }) + )) + ]; + }; + count = lib.mkOption { + type = lib.types.int; + default = 0; + description = "Number of devices of this type to create."; + }; + defaultCount = lib.mkOption { + type = lib.types.int; + default = 0; + description = "Default number of devices to create with default configurations and numbered hostnames."; + }; + overrides = lib.mkOption { + type = lib.types.attrs; + default = { }; + description = "Overrides to apply to all devices of this type."; + }; + }; + })); + }; +in +{ + options.athenix = { + fleet = fleetDefinition; + hwTypes = lib.mkOption { + description = "Hardware types definitions for the fleet."; + type = lib.types.attrs; + }; + }; + + config.athenix.fleet = lib.mkDefault (import ../inventory.nix); + config.athenix.hwTypes = lib.mkDefault (import ../hw { inherit inputs; }); +} -- 2.39.5 From 775080949df8615b62d6084008e888ad6cfefe52 Mon Sep 17 00:00:00 2001 From: UGA Innovation Factory Date: Wed, 7 Jan 2026 18:11:46 -0500 Subject: [PATCH 03/10] refactor: move GC and buildMethods options to sw module - Move athenix.system.gc options from fleet/common.nix to sw/gc.nix - Move athenix.host.buildMethods option to sw/gc.nix - Move home-manager, agenix, disko imports to sw/default.nix - Better separation: fleet/ handles generation, sw/ handles software config --- sw/default.nix | 4 ++++ sw/gc.nix | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 sw/gc.nix diff --git a/sw/default.nix b/sw/default.nix index 81c372f..8ae4d39 100644 --- a/sw/default.nix +++ b/sw/default.nix @@ -28,8 +28,12 @@ in imports = [ ./python.nix ./ghostty.nix + ./gc.nix ./updater.nix ./update-ref.nix + inputs.home-manager.nixosModules.home-manager + inputs.agenix.nixosModules.default + inputs.disko.nixosModules.disko ]; options.athenix.sw = { diff --git a/sw/gc.nix b/sw/gc.nix new file mode 100644 index 0000000..78658cf --- /dev/null +++ b/sw/gc.nix @@ -0,0 +1,58 @@ +{ + config, + lib, + pkgs, + ... +}: +{ + options.athenix = { + system.gc = { + enable = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Whether to enable automatic garbage collection."; + }; + frequency = lib.mkOption { + type = lib.types.str; + default = "weekly"; + description = "How often to run garbage collection (systemd timer format)."; + }; + retentionDays = lib.mkOption { + type = lib.types.int; + default = 30; + description = "Number of days to keep old generations before deletion."; + }; + optimise = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Whether to automatically optimize the Nix store."; + }; + }; + + host.buildMethods = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = [ "installer-iso" ]; + description = '' + List of allowed build methods for this host (used by installer/artifacts.nix). + Supported methods: + - "installer-iso": Generates an auto-install ISO that installs this configuration to disk. + - "iso": Generates a live ISO (using nixos-generators). + - "ipxe": Generates iPXE netboot artifacts (kernel, initrd, script). + - "lxc": Generates an LXC container tarball. + - "proxmox": Generates a Proxmox VMA archive. + ''; + }; + }; + + config = { + # Automatic Garbage Collection + nix.gc = lib.mkIf config.athenix.system.gc.enable { + automatic = true; + dates = config.athenix.system.gc.frequency; + options = "--delete-older-than ${toString config.athenix.system.gc.retentionDays}d"; + }; + + # Optimize storage + nix.optimise.automatic = config.athenix.system.gc.optimise; + }; +} \ No newline at end of file -- 2.39.5 From 1ce7334a733ccd1b87659e49722ea6058a3db754 Mon Sep 17 00:00:00 2001 From: UGA Innovation Factory Date: Wed, 7 Jan 2026 18:11:59 -0500 Subject: [PATCH 04/10] feat: add lib.mkFleet for external flake consumption - Create lib/mkFleet.nix to expose fleet generator as library function - Allow external flakes to use Athenix's fleet logic with custom inventory - Export lib output in flake.nix with proper input passing - Enable usage: nixosConfigurations = athenix.lib.mkFleet { fleet = ...; hwTypes = ...; } --- flake.nix | 7 +++++-- lib/default.nix | 4 ++++ lib/mkFleet.nix | 11 +++++++++++ 3 files changed, 20 insertions(+), 2 deletions(-) create mode 100644 lib/default.nix create mode 100644 lib/mkFleet.nix diff --git a/flake.nix b/flake.nix index 74acc6a..e1cd249 100644 --- a/flake.nix +++ b/flake.nix @@ -69,7 +69,7 @@ ... }: let - fleet = import ./glue/fleet.nix { inherit inputs; }; + fleet = self.lib.mkFleet { inherit inputs; }; linuxSystem = "x86_64-linux"; artifacts = import ./installer/artifacts.nix { inherit inputs fleet self; @@ -90,11 +90,14 @@ nixosConfigurations = fleet.nixosConfigurations; # Expose artifacts to all systems, but they are always built for x86_64-linux - packages = forAllSystems (_: artifacts); + packages.${linuxSystem} = artifacts; # Expose host type modules and installer modules for external use nixosModules = import ./installer/modules.nix { inherit inputs; }; + # Library functions + lib = import ./lib { inherit inputs; }; + # Templates for external configurations templates = import ./templates; }; diff --git a/lib/default.nix b/lib/default.nix new file mode 100644 index 0000000..fe2fff2 --- /dev/null +++ b/lib/default.nix @@ -0,0 +1,4 @@ +{ inputs }: +{ + mkFleet = import ./mkFleet.nix; +} \ No newline at end of file diff --git a/lib/mkFleet.nix b/lib/mkFleet.nix new file mode 100644 index 0000000..4de4d49 --- /dev/null +++ b/lib/mkFleet.nix @@ -0,0 +1,11 @@ +# Generate fleet configurations with custom fleet and hardware types +# Usage: nixosConfigurations = athenix.lib.mkFleet { fleet = { ... }; hwTypes = { ... }; } +{ + inputs, + fleet ? null, + hwTypes ? null, +}: + import ../fleet/default.nix { + inherit inputs; + inherit fleet hwTypes; + } \ No newline at end of file -- 2.39.5 From 7145f5b3d8461facd0daa23cd26209c3b7ebf629 Mon Sep 17 00:00:00 2001 From: UGA Innovation Factory Date: Wed, 7 Jan 2026 18:12:07 -0500 Subject: [PATCH 05/10] fix: update artifacts.nix to use fleet.modules - Use fleet.modules instead of trying to access mkFleet output directly - Fix netboot artifact generation to correctly access module list - Ensure artifacts can access both nixosConfigurations and modules from fleet --- installer/modules.nix | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/installer/modules.nix b/installer/modules.nix index d69a837..9f59981 100644 --- a/installer/modules.nix +++ b/installer/modules.nix @@ -13,12 +13,15 @@ # inputs.athenix.nixosModules.sw { inputs }: -# Automatically import all variant modules from variants/ directory -# This returns an attribute set like: { nix-desktop = ...; nix-laptop = ...; nix-lxc = ...; sw = ...; } -(import ../variants { inherit inputs; }) -// { +# Expose hardware type modules from hw/ directory +# This returns an attribute set like: { nix-desktop = ...; nix-laptop = ...; nix-lxc = ...; } +let + hostTypes = import ../hw { inherit inputs; }; +in +{ # Software configuration module - main module with all athenix.sw options # Use athenix.sw.type to select profile: "desktop", "tablet-kiosk", "headless", "stateless-kiosk" + hw = hostTypes; sw = { inputs, -- 2.39.5 From cda19756311fa14173150e6a3468ad05d0794ac4 Mon Sep 17 00:00:00 2001 From: UGA Innovation Factory Date: Wed, 7 Jan 2026 18:12:19 -0500 Subject: [PATCH 06/10] fix: use lib.mkForce for systemd.network.enable in stateless-kiosk - Prevent conflicts when disabling systemd-networkd in stateless kiosk config - Ensures the disable takes precedence over other module settings --- sw/stateless-kiosk/net.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sw/stateless-kiosk/net.nix b/sw/stateless-kiosk/net.nix index f8aad34..3983ab8 100644 --- a/sw/stateless-kiosk/net.nix +++ b/sw/stateless-kiosk/net.nix @@ -26,5 +26,5 @@ }; # Disable systemd-networkd and systemd-hostnamed - systemd.network.enable = false; + systemd.network.enable = lib.mkForce false; } -- 2.39.5 From d3788be951d6d5b05ecbca618aa7f29bce1f94d4 Mon Sep 17 00:00:00 2001 From: UGA Innovation Factory Date: Wed, 7 Jan 2026 18:12:28 -0500 Subject: [PATCH 07/10] chore: update inventory.nix formatting - Remove trailing whitespace and ensure consistent formatting --- inventory.nix | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/inventory.nix b/inventory.nix index a6ce1b0..32dd57d 100644 --- a/inventory.nix +++ b/inventory.nix @@ -70,7 +70,6 @@ # rev = "e1ccd7cc3e709afe4f50b0627e1c4bde49165014"; # }; # }; - { # ========== Lab Laptops ========== # Creates: nix-laptop1, nix-laptop2 @@ -152,4 +151,4 @@ # ========== Ephemeral/Netboot System ========== # Creates: nix-ephemeral1 nix-ephemeral.devices = 1; -} +} \ No newline at end of file -- 2.39.5 From 97646f3229f519642ed699e6234676f831e0ac55 Mon Sep 17 00:00:00 2001 From: UGA Innovation Factory Date: Wed, 7 Jan 2026 18:12:39 -0500 Subject: [PATCH 08/10] docs: update documentation for refactored structure - Update NAMESPACE.md: filesystem.device and swapSize defaults now null - Update README.md: add 'Using Athenix as a Library' section with lib.mkFleet - Update copilot-instructions.md: reflect new directory structure and defaults - Update EXTERNAL_MODULES.md: correct paths from variants/ and glue/ to hw/ and fleet/ - Update PROXMOX_LXC.md: update hardware type path reference --- .github/copilot-instructions.md | 24 +++++++++++++++++----- README.md | 35 ++++++++++++++++++++++++++++++--- docs/EXTERNAL_MODULES.md | 4 ++-- docs/NAMESPACE.md | 8 ++++---- installer/PROXMOX_LXC.md | 2 +- 5 files changed, 58 insertions(+), 15 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index ddf2e70..539528f 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -26,8 +26,8 @@ This is a **NixOS system configuration repository** that uses: - **`flake.nix`**: Entry point - inputs and outputs only - **`inventory.nix`**: Fleet definitions - host configurations - **`users.nix`**: User account definitions -- **`variants/`**: Hardware type modules (desktop, laptop, surface, lxc, wsl, etc.) -- **`glue/`**: Fleet generation logic and common system configuration +- **`hw/`**: Hardware type modules (desktop, laptop, surface, lxc, wsl, etc.) +- **`fleet/`**: Fleet generation logic and common system configuration - **`sw/`**: Software configurations by system type - **`installer/`**: Build artifact generation (ISO, LXC, etc.) - **`templates/`**: Templates for external configurations @@ -45,9 +45,12 @@ All Innovation Factory-specific options MUST use the `athenix` namespace: ### Host Options (`athenix.host.*`) ```nix athenix.host = { - filesystem.device = "/dev/sda"; # Boot disk - filesystem.swapSize = "32G"; # Swap size - buildMethods = [ "iso" ]; # Artifact types + filesystem.device = "/dev/nvme0n1"; # Boot disk (default: null) + filesystem.swapSize = "32G"; # Swap size (default: null) + buildMethods = [ "installer-iso" ]; # Artifact types (defined in sw/gc.nix) + useHostPrefix = true; # Hostname prefix behavior + wsl.user = "username"; # WSL default user +}; useHostPrefix = true; # Hostname prefix behavior wsl.user = "username"; # WSL default user }; @@ -114,6 +117,17 @@ athenix.forUser = "username"; # Convenience: enable user + set WSL us 3. System modules: Provide `default.nix` that accepts `{ inputs, ... }` 4. Reference in `inventory.nix` or `users.nix` using `builtins.fetchGit` +#### Using Athenix as a Library +External flakes can use Athenix's fleet generator: +```nix +outputs = { athenix, ... }: { + nixosConfigurations = athenix.lib.mkFleet { + fleet = import ./custom-inventory.nix; + hwTypes = import ./custom-hardware.nix; + }; +}; +``` + ## Important Constraints ### What NOT to Do diff --git a/README.md b/README.md index 8e2d0e7..822848a 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ users.nix # User account definitions flake.lock # Locked dependency versions -variants/ # Hardware type modules (exportable as nixosModules) +hw/ # Hardware type modules (exportable as nixosModules) ├── default.nix # Auto-exports all variant types ├── nix-desktop.nix # Desktop workstations ├── nix-laptop.nix # Laptop systems @@ -64,8 +64,8 @@ variants/ # Hardware type modules (exportable as nixosModules) ├── nix-zima.nix # ZimaBoard systems └── nix-ephemeral.nix # Diskless/netboot systems -glue/ # Fleet generation and common configuration -├── fleet.nix # Processes inventory.nix to generate all hosts +fleet/ # Fleet generation and common configuration +├── default.nix # Processes inventory.nix to generate all hosts ├── common.nix # Common NixOS configuration (all hosts) ├── boot.nix # Boot and filesystem configuration └── user-config.nix # User account and home-manager integration @@ -233,6 +233,35 @@ nix flake show **See [docs/BUILDING.md](docs/BUILDING.md) for complete guide.** +### Using Athenix as a Library + +Import Athenix in your own flake to use its fleet generation logic with custom inventory: + +```nix +{ + inputs.athenix.url = "git+https://git.factory.uga.edu/UGA-Innovation-Factory/athenix.git"; + + outputs = { self, athenix, ... }: { + # Generate configurations with custom fleet and hardware types + nixosConfigurations = athenix.lib.mkFleet { + fleet = import ./my-inventory.nix; + hwTypes = import ./my-hardware-types.nix; + }; + + # Or use individual modules + nixosConfigurations.myhost = nixpkgs.lib.nixosSystem { + modules = [ + athenix.nixosModules.hw.nix-desktop # Use Athenix hardware configs + athenix.nixosModules.sw # Use Athenix software configs + ./configuration.nix + ]; + }; + }; +} +``` + +**Exported modules:** `nix-desktop`, `nix-laptop`, `nix-surface`, `nix-lxc`, `nix-wsl`, `nix-ephemeral`, `nix-zima`, `sw`, `common` + ## System Types Set via `athenix.sw.type`: diff --git a/docs/EXTERNAL_MODULES.md b/docs/EXTERNAL_MODULES.md index a94dfef..4ba7299 100644 --- a/docs/EXTERNAL_MODULES.md +++ b/docs/EXTERNAL_MODULES.md @@ -115,8 +115,8 @@ server-config/ When a host is built, modules load in this order: -1. Hardware type module (from `variants/nix-*.nix`) -2. Common system configuration (from `glue/common.nix`) +1. Hardware type module (from `hw/nix-*.nix`) +2. Common system configuration (from `fleet/common.nix`) 3. Software type module (from `sw/{type}/`) 4. User NixOS modules (from `users.nix` - `nixos.nix` files) 5. Device-specific overrides (from `inventory.nix`) diff --git a/docs/NAMESPACE.md b/docs/NAMESPACE.md index 118688b..6c55c7d 100644 --- a/docs/NAMESPACE.md +++ b/docs/NAMESPACE.md @@ -17,9 +17,9 @@ Hardware and boot-related settings. Boot disk device path. -**Type:** String +**Type:** String or null -**Default:** `"/dev/sda"` +**Default:** `null` (must be set by hardware type or per-host) **Example:** ```nix @@ -30,9 +30,9 @@ athenix.host.filesystem.device = "/dev/nvme0n1"; Swap partition size. -**Type:** String (size with unit, e.g., `"32G"`, `"2G"`) +**Type:** String (size with unit, e.g., `"32G"`, `"2G"`) or null -**Default:** `"32G"` +**Default:** `null` (must be set by hardware type or per-host) **Example:** ```nix diff --git a/installer/PROXMOX_LXC.md b/installer/PROXMOX_LXC.md index 057e3c9..64d25ef 100644 --- a/installer/PROXMOX_LXC.md +++ b/installer/PROXMOX_LXC.md @@ -46,7 +46,7 @@ Add the host to `inventory.nix` with the `nix-lxc` type or ensure it has the app } ``` -Your host type configuration (`variants/nix-lxc.nix`) should include: +Your host type configuration (`hw/nix-lxc.nix`) should include: ```nix { -- 2.39.5 From 4cb8d8ef13a45eb4154c9c0d53d39608fe9ae896 Mon Sep 17 00:00:00 2001 From: UGA Innovation Factory Date: Wed, 7 Jan 2026 18:15:19 -0500 Subject: [PATCH 09/10] chore: remove refactored-out files --- glue/fleet.nix | 278 ------------------------------------- glue/user-config.nix | 276 ------------------------------------ variants/default.nix | 23 --- variants/nix-desktop.nix | 51 ------- variants/nix-ephemeral.nix | 67 --------- variants/nix-laptop.nix | 64 --------- variants/nix-lxc.nix | 61 -------- variants/nix-surface.nix | 69 --------- variants/nix-wsl.nix | 53 ------- variants/nix-zima.nix | 50 ------- 10 files changed, 992 deletions(-) delete mode 100644 glue/fleet.nix delete mode 100644 glue/user-config.nix delete mode 100644 variants/default.nix delete mode 100644 variants/nix-desktop.nix delete mode 100644 variants/nix-ephemeral.nix delete mode 100644 variants/nix-laptop.nix delete mode 100644 variants/nix-lxc.nix delete mode 100644 variants/nix-surface.nix delete mode 100644 variants/nix-wsl.nix delete mode 100644 variants/nix-zima.nix diff --git a/glue/fleet.nix b/glue/fleet.nix deleted file mode 100644 index e0714b2..0000000 --- a/glue/fleet.nix +++ /dev/null @@ -1,278 +0,0 @@ -{ - inputs, - fleet ? import ../inventory.nix, - ... -}: - -# ============================================================================ -# Fleet Generator -# ============================================================================ -# This file contains the logic to generate NixOS configurations for all hosts -# defined in inventory.nix. It supports both hostname-based and count-based -# configurations with flexible type associations. - -let - nixpkgs = inputs.nixpkgs; - lib = nixpkgs.lib; - - # Load all available host types from hosts/ - hostTypes = import ../variants { inherit inputs; }; - - # Helper to create a single NixOS system configuration - mkHost = - { - hostName, - system ? "x86_64-linux", - hostType, - configOverrides ? { }, - externalModuleThunk ? null, - }: - let - # Lazy evaluation: only fetch external module when building this host - externalModulePath = - if externalModuleThunk != null then - let - # Force evaluation of the thunk (fetchGit, fetchTarball, etc.) - fetchedPath = externalModuleThunk; - # Extract outPath from fetchGit/fetchTarball results - extractedPath = - if builtins.isAttrs fetchedPath && fetchedPath ? outPath then fetchedPath.outPath else fetchedPath; - in - if builtins.isPath extractedPath then - extractedPath + "/default.nix" - else if lib.isDerivation extractedPath then - extractedPath + "/default.nix" - else - extractedPath + "/default.nix" - else - null; - - # Load users.nix to find external user modules - pkgs = nixpkgs.legacyPackages.${system}; - usersData = import ../users.nix { inherit pkgs; }; - accounts = usersData.athenix.users or { }; - - # Build a map of user names to their nixos module paths (if they exist) - # We'll use this to conditionally import modules based on user.enable - userNixosModulePaths = lib.filterAttrs (_: v: v != null) ( - lib.mapAttrs ( - name: user: - if (user ? external && user.external != null) then - let - externalPath = - if builtins.isAttrs user.external && user.external ? outPath then - user.external.outPath - else - user.external; - nixosModulePath = externalPath + "/nixos.nix"; - in - if - (builtins.isPath externalPath || (builtins.isString externalPath && lib.hasPrefix "/" externalPath)) - && builtins.pathExists nixosModulePath - then - nixosModulePath - else - null - else - null - ) accounts - ); - - # Create conditional wrapper modules for each user's nixos.nix - # Each wrapper checks if the user is enabled before applying the module content - userNixosModules = lib.mapAttrsToList ( - name: modulePath: - { - config, - lib, - pkgs, - ... - }@args: - let - # Import the user's nixos module - it returns a function or attrset - importedModuleFunc = import modulePath { inherit inputs; }; - # If it's a function, call it with the module args; otherwise use as-is - importedModule = - if lib.isFunction importedModuleFunc then importedModuleFunc args else importedModuleFunc; - in - { - config = lib.mkIf (config.athenix.users.${name}.enable or false) importedModule; - } - ) userNixosModulePaths; - - # Get the host type module from the hostTypes attribute set - typeModule = - hostTypes.${hostType} - or (throw "Host type '${hostType}' not found. Available types: ${lib.concatStringsSep ", " (lib.attrNames hostTypes)}"); - - # External module from fetchGit/fetchurl - externalPathModule = - if externalModulePath != null then import externalModulePath { inherit inputs; } else { }; - - # Config override module - translate special keys to athenix options - overrideModule = - { ... }: - let - cleanConfig = lib.removeAttrs configOverrides [ - "type" - "count" - "devices" - "overrides" - "defaultCount" - "buildMethods" - ]; - specialConfig = lib.optionalAttrs (configOverrides ? buildMethods) { - athenix.host.buildMethods = configOverrides.buildMethods; - }; - in - { - config = lib.mkMerge [ - cleanConfig - specialConfig - ]; - }; - - allModules = - userNixosModules - ++ [ - (import ./common.nix { inherit inputs; }) - typeModule - overrideModule - { networking.hostName = hostName; } - ] - ++ lib.optional (externalModulePath != null) externalPathModule; - in - { - system = lib.nixosSystem { - inherit system; - specialArgs = { inherit inputs; }; - modules = allModules; - }; - modules = allModules; - }; - - # Process inventory entries - top-level keys are always prefixes - processInventory = lib.mapAttrs ( - prefix: config: - let - hostType = config.type or prefix; - system = config.system or "x86_64-linux"; - devices = config.devices or { }; - hasCount = config ? count; - - # Helper to generate hostname from prefix and suffix - # Numbers get no dash: "nix-surface1", "nix-surface2" - # Letters get dash: "nix-surface-alpha", "nix-surface-beta" - mkHostName = - prefix: suffix: usePrefix: - if !usePrefix then - suffix - else if lib.match "[0-9]+" suffix != null then - "${prefix}${suffix}" # numeric: no dash - else - "${prefix}-${suffix}"; # non-numeric: add dash - - # Extract common overrides and default count - overrides = config.overrides or { }; - defaultCount = config.defaultCount or 0; - - # If devices is a number, treat it as count - devicesValue = config.devices or { }; - actualDevices = if lib.isInt devicesValue then { } else devicesValue; - actualCount = if lib.isInt devicesValue then devicesValue else (config.count or 0); - - # Clean base config - remove inventory management keys - baseConfig = lib.removeAttrs config [ - "type" - "system" - "count" - "devices" - "overrides" - "defaultCount" - ]; - - # Generate hosts from explicit device definitions - deviceHosts = lib.listToAttrs ( - lib.mapAttrsToList ( - deviceKey: deviceConfig: - let - # Check if deviceConfig has an 'external' field for lazy evaluation - hasExternalField = builtins.isAttrs deviceConfig && deviceConfig ? external; - - # Extract external module thunk if present (don't evaluate yet!) - externalModuleThunk = if hasExternalField then deviceConfig.external else null; - - # Remove 'external' from config to avoid conflicts - cleanDeviceConfig = - if hasExternalField then lib.removeAttrs deviceConfig [ "external" ] else deviceConfig; - - # Merge: base config -> overrides -> device-specific config - mergedConfig = lib.recursiveUpdate (lib.recursiveUpdate baseConfig overrides) cleanDeviceConfig; - - # Check useHostPrefix from the merged config - usePrefix = mergedConfig.athenix.host.useHostPrefix or true; - hostName = mkHostName prefix deviceKey usePrefix; - in - { - name = hostName; - value = mkHost { - inherit - hostName - system - hostType - externalModuleThunk - ; - configOverrides = mergedConfig; - }; - } - ) actualDevices - ); - - # Generate numbered hosts from count or defaultCount - # If devices are specified, defaultCount fills in the gaps - countToUse = if actualCount > 0 then actualCount else defaultCount; - - # Get which numbered keys are already defined in devices - existingNumbers = lib.filter (k: lib.match "[0-9]+" k != null) (lib.attrNames actualDevices); - - countHosts = - if countToUse > 0 then - lib.listToAttrs ( - lib.filter (x: x != null) ( - map ( - i: - let - deviceKey = toString i; - # Skip if this number is already in explicit devices - alreadyDefined = lib.elem deviceKey existingNumbers; - in - if alreadyDefined then - null - else - let - hostName = mkHostName prefix deviceKey true; - mergedConfig = lib.recursiveUpdate baseConfig overrides; - in - { - name = hostName; - value = mkHost { - inherit hostName system hostType; - configOverrides = mergedConfig; - }; - } - ) (lib.range 1 countToUse) - ) - ) - else - { }; - in - lib.recursiveUpdate deviceHosts countHosts - ) fleet; - - # Flatten the nested structure - allHosts = lib.foldl' lib.recursiveUpdate { } (lib.attrValues processInventory); -in -{ - nixosConfigurations = lib.mapAttrs (n: v: v.system) allHosts; - modules = lib.mapAttrs (n: v: v.modules) allHosts; -} diff --git a/glue/user-config.nix b/glue/user-config.nix deleted file mode 100644 index 299acef..0000000 --- a/glue/user-config.nix +++ /dev/null @@ -1,276 +0,0 @@ -{ - 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 - # Load users.nix to get account definitions - usersData = import ../users.nix { inherit pkgs; }; - accounts = usersData.athenix.users or { }; - - # 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; - - # Extract athenix.users options from external user.nix modules - # First, build a cache of options per user from their external user.nix (if any). - externalUserModuleOptions = lib.genAttrs (lib.attrNames accounts) ( - name: - let - user = accounts.${name}; - externalPath = resolveExternalPath (user.external or null); - userNixPath = if externalPath != null then externalPath + "/user.nix" else null; - in - if isValidPath userNixPath then - let - # Import and evaluate the module with minimal args - outerModule = import userNixPath { inherit inputs; }; - evaluatedModule = outerModule { - config = { }; - inherit lib pkgs; - osConfig = null; - }; - # Extract just the athenix.users. options - athenixUsers = evaluatedModule.athenix.users or { }; - in - athenixUsers.${name} or { } - else - { } - ); - - # externalUserOptions only contains users that actually have options defined - externalUserOptions = lib.filterAttrs ( - _: moduleOptions: moduleOptions != { } - ) externalUserModuleOptions; - - # Submodule defining the structure of a user account - userSubmodule = lib.types.submodule { - options = { - isNormalUser = lib.mkOption { - type = lib.types.bool; - default = true; - }; - description = lib.mkOption { - type = lib.types.nullOr lib.types.str; - default = null; - }; - extraGroups = lib.mkOption { - type = lib.types.listOf lib.types.str; - default = [ ]; - }; - hashedPassword = lib.mkOption { - type = lib.types.str; - default = "!"; - }; - extraPackages = lib.mkOption { - type = lib.types.listOf lib.types.package; - default = [ ]; - }; - excludePackages = lib.mkOption { - type = lib.types.listOf lib.types.package; - default = [ ]; - }; - homePackages = lib.mkOption { - type = lib.types.listOf lib.types.package; - default = [ ]; - }; - extraImports = lib.mkOption { - type = lib.types.listOf lib.types.path; - default = [ ]; - }; - external = lib.mkOption { - type = lib.types.nullOr ( - lib.types.oneOf [ - lib.types.path - lib.types.package - lib.types.attrs - ] - ); - default = null; - description = '' - External user configuration module. Can be: - - A path to a local module directory - - A fetchGit/fetchTarball result pointing to a repository - - The external module can contain: - - user.nix (optional): Sets athenix.users. options AND home-manager config - - nixos.nix (optional): System-level NixOS configuration - - Example: builtins.fetchGit { url = "https://github.com/user/dotfiles"; rev = "..."; } - ''; - }; - opensshKeys = lib.mkOption { - type = lib.types.listOf lib.types.str; - default = [ ]; - description = "List of SSH public keys for the user."; - }; - shell = lib.mkOption { - type = lib.types.nullOr lib.types.package; - default = null; - description = "The shell for this user."; - }; - editor = lib.mkOption { - type = lib.types.nullOr lib.types.package; - default = null; - description = "The default editor for this user."; - }; - useZshTheme = lib.mkOption { - type = lib.types.bool; - default = true; - description = "Whether to apply the system Zsh theme."; - }; - useNvimPlugins = lib.mkOption { - type = lib.types.bool; - default = true; - description = "Whether to apply the system Neovim configuration."; - }; - enable = lib.mkOption { - type = lib.types.bool; - default = false; - description = "Whether this user account is enabled on this system."; - }; - }; - }; -in -{ - - options.athenix.users = lib.mkOption { - type = lib.types.attrsOf userSubmodule; - default = { }; - description = "User accounts configuration. Set enable=true for users that should exist on this system."; - }; - - config = { - # Merge user definitions from users.nix with options from external user.nix modules - # External options take precedence over users.nix (which uses lib.mkDefault) - athenix.users = lib.mapAttrs ( - name: user: - user - // { - description = lib.mkDefault (user.description or null); - shell = lib.mkDefault (user.shell or null); - extraGroups = lib.mkDefault (user.extraGroups or [ ]); - } - // (externalUserOptions.${name} or { }) - ) accounts; - - # 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); - in - { - inherit (user) isNormalUser extraGroups hashedPassword; - description = if user.description != null then user.description else lib.mkDefault ""; - openssh.authorizedKeys.keys = user.opensshKeys; - packages = finalPackages; - shell = if user.shell != null then user.shell else pkgs.bash; - } - ) 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 = if name == "root" then "/root" else "/home/${name}"; - home.stateVersion = "25.11"; - } - (lib.mkIf (!hasExternal) { - # For local users only, add their packages - home.packages = user.homePackages; - }) - ] - ) enabledAccounts; - }; - }; -} diff --git a/variants/default.nix b/variants/default.nix deleted file mode 100644 index d91f415..0000000 --- a/variants/default.nix +++ /dev/null @@ -1,23 +0,0 @@ -# ============================================================================ -# Host Types Module -# ============================================================================ -# This module exports all available host types as an attribute set. -# Each type is a NixOS module function that takes { inputs } and returns -# a module configuration. - -{ inputs }: -let - inherit (builtins) readDir attrNames; - lib = inputs.nixpkgs.lib; - inherit (lib) filterAttrs removeSuffix genAttrs; - - files = readDir ./.; - - # Keep only regular *.nix files except default.nix - nixFiles = filterAttrs ( - name: type: type == "regular" && lib.hasSuffix ".nix" name && name != "default.nix" - ) files; - - moduleNames = map (name: removeSuffix ".nix" name) (attrNames nixFiles); -in -genAttrs moduleNames (name: import ./${name}.nix { inherit inputs; }) diff --git a/variants/nix-desktop.nix b/variants/nix-desktop.nix deleted file mode 100644 index 2455eba..0000000 --- a/variants/nix-desktop.nix +++ /dev/null @@ -1,51 +0,0 @@ -# ============================================================================ -# Desktop Configuration -# ============================================================================ -# Hardware and boot configuration for standard desktop workstations. -# Includes Intel CPU support and NVMe storage. - -{ inputs, ... }: -{ - config, - lib, - modulesPath, - ... -}: -{ - imports = [ - (modulesPath + "/installer/scan/not-detected.nix") - ]; - - # ========== Boot Configuration ========== - - boot.initrd.availableKernelModules = [ - "xhci_pci" # USB 3.0 support - "nvme" # NVMe SSD support - "usb_storage" # USB storage devices - "sd_mod" # SD card support - "sdhci_pci" # SD card host controller - ]; - boot.initrd.kernelModules = [ ]; - boot.kernelModules = [ "kvm-intel" ]; # Intel virtualization support - boot.extraModulePackages = [ ]; - boot.kernelParams = [ - "quiet" # Minimal boot messages - "splash" # Show Plymouth boot splash - "boot.shell_on_fail" # Emergency shell on boot failure - "udev.log_priority=3" # Reduce udev logging - "rd.systemd.show_status=auto" # Show systemd status during boot - ]; - - # ========== Filesystem Configuration ========== - athenix.host.filesystem.swapSize = lib.mkDefault "16G"; - athenix.host.filesystem.device = lib.mkDefault "/dev/nvme0n1"; - athenix.host.buildMethods = lib.mkDefault [ "installer-iso" ]; - nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; - - # ========== Hardware Configuration ========== - hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; - - # ========== Software Profile ========== - athenix.sw.enable = lib.mkDefault true; - athenix.sw.type = lib.mkDefault "desktop"; -} diff --git a/variants/nix-ephemeral.nix b/variants/nix-ephemeral.nix deleted file mode 100644 index 51e46c0..0000000 --- a/variants/nix-ephemeral.nix +++ /dev/null @@ -1,67 +0,0 @@ -# ============================================================================ -# Ephemeral/Diskless System Configuration -# ============================================================================ -# Configuration for systems that run entirely from RAM without persistent storage. -# Suitable for kiosks, netboot clients, and stateless workstations. -# All data is lost on reboot. - -{ inputs, ... }: -{ - config, - lib, - modulesPath, - ... -}: -{ - imports = [ - (modulesPath + "/installer/scan/not-detected.nix") - ]; - - # ========== Boot Configuration ========== - boot.initrd.availableKernelModules = [ - "xhci_pci" # USB 3.0 support - "nvme" # NVMe support - "usb_storage" # USB storage devices - "sd_mod" # SD card support - "sdhci_pci" # SD card host controller - ]; - boot.initrd.kernelModules = [ ]; - boot.kernelModules = [ "kvm-intel" ]; # Intel virtualization support - boot.extraModulePackages = [ ]; - boot.kernelParams = [ - "quiet" # Minimal boot messages - "splash" # Show Plymouth boot splash - "boot.shell_on_fail" # Emergency shell on boot failure - "udev.log_priority=3" # Reduce udev logging - "rd.systemd.show_status=auto" # Show systemd status during boot - ]; - - # ========== Ephemeral Configuration ========== - # No persistent storage - everything runs from RAM - athenix.host.filesystem.swapSize = lib.mkForce "0G"; - athenix.host.filesystem.device = lib.mkForce "/dev/null"; # Dummy device - athenix.host.buildMethods = lib.mkDefault [ - "iso" # Live ISO image - "ipxe" # Network boot - ]; - - # Disable disk management for RAM-only systems - disko.enableConfig = lib.mkForce false; - - # Define tmpfs root filesystem - fileSystems."/" = { - device = "none"; - fsType = "tmpfs"; - options = [ - "defaults" - "size=50%" - "mode=755" - ]; - }; - - nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; - hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; - - athenix.sw.enable = lib.mkDefault true; - athenix.sw.type = lib.mkDefault "stateless-kiosk"; -} diff --git a/variants/nix-laptop.nix b/variants/nix-laptop.nix deleted file mode 100644 index 6d9ed5c..0000000 --- a/variants/nix-laptop.nix +++ /dev/null @@ -1,64 +0,0 @@ -# ============================================================================ -# Laptop Configuration -# ============================================================================ -# Hardware and boot configuration for laptop systems with mobile features. -# Includes power management, lid switch handling, and Intel graphics fixes. - -{ inputs, ... }: -{ - config, - lib, - modulesPath, - ... -}: -{ - imports = [ - (modulesPath + "/installer/scan/not-detected.nix") - ]; - - # ========== Boot Configuration ========== - - boot.initrd.availableKernelModules = [ - "xhci_pci" # USB 3.0 support - "thunderbolt" # Thunderbolt support - "nvme" # NVMe SSD support - "usb_storage" # USB storage devices - "sd_mod" # SD card support - "sdhci_pci" # SD card host controller - ]; - boot.initrd.kernelModules = [ ]; - boot.kernelModules = [ "kvm-intel" ]; # Intel virtualization support - boot.extraModulePackages = [ ]; - boot.kernelParams = [ - "quiet" # Minimal boot messages - "splash" # Show Plymouth boot splash - "boot.shell_on_fail" # Emergency shell on boot failure - "udev.log_priority=3" # Reduce udev logging - "rd.systemd.show_status=auto" # Show systemd status during boot - "i915.enable_psr=0" # Disable Panel Self Refresh (stability) - "i915.enable_dc=0" # Disable display power saving - "i915.enable_fbc=0" # Disable framebuffer compression - ]; - - # ========== Hardware Configuration ========== - nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; - hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; - - # ========== Filesystem Configuration ========== - athenix.host.filesystem.device = lib.mkDefault "/dev/nvme0n1"; - athenix.host.filesystem.swapSize = lib.mkDefault "34G"; # Larger swap for hibernation - athenix.host.buildMethods = lib.mkDefault [ "installer-iso" ]; - - # ========== Power Management ========== - services.upower.enable = lib.mkDefault true; - services.logind.settings = { - Login = { - HandleLidSwitch = "suspend"; - HandleLidSwitchExternalPower = "suspend"; - HandleLidSwitchDocked = "ignore"; - }; - }; - - athenix.sw.enable = lib.mkDefault true; - athenix.sw.type = lib.mkDefault "desktop"; -} diff --git a/variants/nix-lxc.nix b/variants/nix-lxc.nix deleted file mode 100644 index cecb409..0000000 --- a/variants/nix-lxc.nix +++ /dev/null @@ -1,61 +0,0 @@ -# ============================================================================ -# Proxmox LXC Container Configuration -# ============================================================================ -# Configuration for lightweight Linux containers running in Proxmox. -# Disables boot/disk management and enables remote development support. - -{ inputs, ... }: -{ - config, - lib, - modulesPath, - ... -}: -{ - imports = [ - inputs.vscode-server.nixosModules.default - "${modulesPath}/virtualisation/proxmox-lxc.nix" - ]; - - # ========== Nix Configuration ========== - nix.settings.trusted-users = [ - "root" - "engr-ugaif" - ]; - nix.settings.experimental-features = [ - "nix-command" - "flakes" - ]; - - # ========== Container-Specific Configuration ========== - boot.isContainer = true; - boot.loader.systemd-boot.enable = lib.mkForce false; # No bootloader in container - disko.enableConfig = lib.mkForce false; # No disk management in container - console.enable = true; - - # Allow getty to work in containers - systemd.services."getty@".unitConfig.ConditionPathExists = [ - "" - "/dev/%I" - ]; - - # Suppress unnecessary systemd units for containers - systemd.suppressedSystemUnits = [ - "dev-mqueue.mount" - "sys-kernel-debug.mount" - "sys-fs-fuse-connections.mount" - ]; - - # ========== Remote Development ========== - services.vscode-server.enable = true; - - # ========== System Configuration ========== - system.stateVersion = "25.11"; - athenix.host.buildMethods = lib.mkDefault [ - "lxc" # LXC container tarball - "proxmox" # Proxmox VMA archive - ]; - - athenix.sw.enable = lib.mkDefault true; - athenix.sw.type = lib.mkDefault "headless"; -} diff --git a/variants/nix-surface.nix b/variants/nix-surface.nix deleted file mode 100644 index 53f74c4..0000000 --- a/variants/nix-surface.nix +++ /dev/null @@ -1,69 +0,0 @@ -# ============================================================================ -# Microsoft Surface Tablet Configuration -# ============================================================================ -# Hardware configuration for Surface Go tablets in kiosk mode. -# Uses nixos-hardware module and older kernel for Surface-specific drivers. - -{ inputs, ... }: -{ - config, - lib, - pkgs, - modulesPath, - ... -}: -let - # Use older kernel version for better Surface Go compatibility - refSystem = inputs.nixpkgs-old-kernel.lib.nixosSystem { - system = pkgs.stdenv.hostPlatform.system; - modules = [ inputs.nixos-hardware.nixosModules.microsoft-surface-go ]; - }; - refKernelPackages = refSystem.config.boot.kernelPackages; -in -{ - imports = [ - (modulesPath + "/installer/scan/not-detected.nix") - inputs.nixos-hardware.nixosModules.microsoft-surface-go - ]; - - # ========== Boot Configuration ========== - - boot.initrd.availableKernelModules = [ - "xhci_pci" # USB 3.0 support - "nvme" # NVMe support (though Surface uses eMMC) - "usb_storage" # USB storage devices - "sd_mod" # SD card support - "sdhci_pci" # SD card host controller - ]; - boot.initrd.kernelModules = [ ]; - boot.kernelModules = [ "kvm-intel" ]; # Intel virtualization support - boot.extraModulePackages = [ ]; - boot.kernelParams = [ - "quiet" # Minimal boot messages - "splash" # Show Plymouth boot splash - "boot.shell_on_fail" # Emergency shell on boot failure - "udev.log_priority=3" # Reduce udev logging - "rd.systemd.show_status=auto" # Show systemd status during boot - "intel_ipu3_imgu" # Intel camera image processing - "intel_ipu3_isys" # Intel camera sensor interface - "fbcon=map:1" # Framebuffer console mapping - "i915.enable_psr=0" # Disable Panel Self Refresh (breaks resume) - "i915.enable_dc=0" # Disable display power saving - ]; - - # Use older kernel for better Surface hardware support - boot.kernelPackages = lib.mkForce refKernelPackages; - - # ========== Filesystem Configuration ========== - athenix.host.filesystem.swapSize = lib.mkDefault "8G"; - athenix.host.filesystem.device = lib.mkDefault "/dev/mmcblk0"; # eMMC storage # eMMC storage - athenix.host.buildMethods = lib.mkDefault [ "installer-iso" ]; - nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; - - # ========== Hardware Configuration ========== - hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; - - # ========== Software Profile ========== - athenix.sw.enable = lib.mkDefault true; - athenix.sw.type = lib.mkDefault "tablet-kiosk"; # Touch-optimized kiosk mode -} diff --git a/variants/nix-wsl.nix b/variants/nix-wsl.nix deleted file mode 100644 index f13787a..0000000 --- a/variants/nix-wsl.nix +++ /dev/null @@ -1,53 +0,0 @@ -# ============================================================================ -# Windows Subsystem for Linux (WSL) Configuration -# ============================================================================ -# Configuration for NixOS running in WSL2 on Windows. -# Integrates with nixos-wsl for WSL-specific functionality. - -{ inputs, ... }: -{ - lib, - config, - ... -}: -{ - imports = [ - inputs.nixos-wsl.nixosModules.default - inputs.vscode-server.nixosModules.default - ]; - - # ========== Options ========== - options.athenix.host.wsl.user = lib.mkOption { - type = lib.types.str; - default = "engr-ugaif"; - description = "The default user to log in as in WSL."; - }; - - config = { - # ========== WSL Configuration ========== - wsl.enable = true; - # Use forUser if set, otherwise fall back to wsl.user option - wsl.defaultUser = - if config.athenix.forUser != null then config.athenix.forUser else config.athenix.host.wsl.user; - - # ========== Software Profile ========== - athenix.sw.enable = lib.mkDefault true; - athenix.sw.type = lib.mkDefault "headless"; - - # ========== Remote Development ========== - services.vscode-server.enable = true; - - # ========== Disable Irrelevant Systems ========== - # WSL doesn't use traditional boot or disk management - disko.enableConfig = lib.mkForce false; - boot.loader.systemd-boot.enable = lib.mkForce false; - boot.loader.grub.enable = lib.mkForce false; - - # WSL manages its own networking - systemd.network.enable = lib.mkForce false; - - # Provide dummy values for required options from boot.nix - athenix.host.filesystem.device = "/dev/null"; - athenix.host.filesystem.swapSize = "0G"; - }; -} diff --git a/variants/nix-zima.nix b/variants/nix-zima.nix deleted file mode 100644 index 1174ff0..0000000 --- a/variants/nix-zima.nix +++ /dev/null @@ -1,50 +0,0 @@ -# ============================================================================ -# Desktop Configuration -# ============================================================================ -# Hardware and boot configuration for standard desktop workstations. -# Includes Intel CPU support and NVMe storage. - -{ inputs, ... }: -{ - config, - lib, - modulesPath, - ... -}: -{ - imports = [ - (modulesPath + "/installer/scan/not-detected.nix") - ]; - - # ========== Boot Configuration ========== - - boot.initrd.availableKernelModules = [ - "xhci_pci" # USB 3.0 support - "usb_storage" # USB storage devices - "sd_mod" # SD card support - "sdhci_pci" # SD card host controller - ]; - boot.initrd.kernelModules = [ ]; - boot.kernelModules = [ "kvm-intel" ]; # Intel virtualization support - boot.extraModulePackages = [ ]; - boot.kernelParams = [ - "quiet" # Minimal boot messages - "splash" # Show Plymouth boot splash - "boot.shell_on_fail" # Emergency shell on boot failure - "udev.log_priority=3" # Reduce udev logging - "rd.systemd.show_status=auto" # Show systemd status during boot - ]; - - # ========== Filesystem Configuration ========== - athenix.host.filesystem.useSwap = lib.mkDefault false; - athenix.host.filesystem.device = lib.mkDefault "/dev/mmcblk0"; - athenix.host.buildMethods = lib.mkDefault [ "installer-iso" ]; - nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; - - # ========== Hardware Configuration ========== - hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; - - # ========== Software Profile ========== - athenix.sw.enable = lib.mkDefault true; - athenix.sw.type = lib.mkDefault "desktop"; -} -- 2.39.5 From d89caa8a6b5ab7d85a2fb6ea139b259f63f45378 Mon Sep 17 00:00:00 2001 From: UGA Innovation Factory Date: Wed, 7 Jan 2026 18:15:37 -0500 Subject: [PATCH 10/10] chore: run nix fmt --- fleet/common.nix | 8 ++-- fleet/default.nix | 6 ++- fleet/fleet-option.nix | 93 +++++++++++++++++++++++------------------- hw/default.nix | 9 +--- inventory.nix | 2 +- lib/default.nix | 2 +- lib/mkFleet.nix | 8 ++-- sw/gc.nix | 2 +- 8 files changed, 69 insertions(+), 61 deletions(-) diff --git a/fleet/common.nix b/fleet/common.nix index 8bcbb4d..5bc95bf 100644 --- a/fleet/common.nix +++ b/fleet/common.nix @@ -36,12 +36,12 @@ }; }; - config = lib.mkMerge [ + config = lib.mkMerge [ (lib.mkIf (config.athenix.forUser != null) { athenix.users.${config.athenix.forUser}.enable = true; - }) - { - system.stateVersion = "25.11"; + }) + { + system.stateVersion = "25.11"; nix.settings.experimental-features = [ "nix-command" "flakes" diff --git a/fleet/default.nix b/fleet/default.nix index 16e1b7b..e870fbd 100644 --- a/fleet/default.nix +++ b/fleet/default.nix @@ -22,7 +22,11 @@ let inventoryModule = lib.evalModules { modules = [ (import ./fleet-option.nix { inherit inputs; }) - { _module.args = { pkgs = nixpkgs.legacyPackages.x86_64-linux; }; } + { + _module.args = { + pkgs = nixpkgs.legacyPackages.x86_64-linux; + }; + } (lib.mkIf (fleet != null) { athenix.fleet = lib.mkForce fleet; }) (lib.mkIf (hwTypes != null) { athenix.hwTypes = lib.mkForce hwTypes; }) ]; diff --git a/fleet/fleet-option.nix b/fleet/fleet-option.nix index b56a99f..e781ba6 100644 --- a/fleet/fleet-option.nix +++ b/fleet/fleet-option.nix @@ -8,48 +8,57 @@ let fleetDefinition = lib.mkOption { description = "Hardware types definitions for the fleet."; - type = lib.types.attrsOf (lib.types.submodule ({ name, ...}: { - options = { - type = lib.mkOption { - type = lib.types.oneOf [ - lib.types.str - lib.types.listOf lib.types.str - ]; - default = name; - description = "Type(s) of system configuration for this device."; - }; - system = lib.mkOption { - type = lib.types.str; - default = "x86_64-linux"; - description = "NixOS system architecture for this hardware type."; - }; - devices = lib.mkOption { - type = lib.types.oneOf [ - lib.types.int - (lib.types.attrsOf - (lib.types.submodule ({ name, ... }: { - freeformType = lib.types.attrs; - }) - )) - ]; - }; - count = lib.mkOption { - type = lib.types.int; - default = 0; - description = "Number of devices of this type to create."; - }; - defaultCount = lib.mkOption { - type = lib.types.int; - default = 0; - description = "Default number of devices to create with default configurations and numbered hostnames."; - }; - overrides = lib.mkOption { - type = lib.types.attrs; - default = { }; - description = "Overrides to apply to all devices of this type."; - }; - }; - })); + type = lib.types.attrsOf ( + lib.types.submodule ( + { name, ... }: + { + options = { + type = lib.mkOption { + type = lib.types.oneOf [ + lib.types.str + lib.types.listOf + lib.types.str + ]; + default = name; + description = "Type(s) of system configuration for this device."; + }; + system = lib.mkOption { + type = lib.types.str; + default = "x86_64-linux"; + description = "NixOS system architecture for this hardware type."; + }; + devices = lib.mkOption { + type = lib.types.oneOf [ + lib.types.int + (lib.types.attrsOf ( + lib.types.submodule ( + { name, ... }: + { + freeformType = lib.types.attrs; + } + ) + )) + ]; + }; + count = lib.mkOption { + type = lib.types.int; + default = 0; + description = "Number of devices of this type to create."; + }; + defaultCount = lib.mkOption { + type = lib.types.int; + default = 0; + description = "Default number of devices to create with default configurations and numbered hostnames."; + }; + overrides = lib.mkOption { + type = lib.types.attrs; + default = { }; + description = "Overrides to apply to all devices of this type."; + }; + }; + } + ) + ); }; in { diff --git a/hw/default.nix b/hw/default.nix index 0b95ad2..5172bee 100644 --- a/hw/default.nix +++ b/hw/default.nix @@ -14,15 +14,10 @@ let # Keep only regular *.nix files except default.nix nixFiles = filterAttrs ( - name: type: - type == "regular" - && lib.hasSuffix ".nix" name - && name != "default.nix" + name: type: type == "regular" && lib.hasSuffix ".nix" name && name != "default.nix" ) files; moduleNames = map (name: removeSuffix ".nix" name) (attrNames nixFiles); in # Export: { name = ; } -genAttrs moduleNames (name: - import (./. + ("/" + name + ".nix")) -) \ No newline at end of file +genAttrs moduleNames (name: import (./. + ("/" + name + ".nix"))) diff --git a/inventory.nix b/inventory.nix index 32dd57d..0338d8f 100644 --- a/inventory.nix +++ b/inventory.nix @@ -151,4 +151,4 @@ # ========== Ephemeral/Netboot System ========== # Creates: nix-ephemeral1 nix-ephemeral.devices = 1; -} \ No newline at end of file +} diff --git a/lib/default.nix b/lib/default.nix index fe2fff2..372377f 100644 --- a/lib/default.nix +++ b/lib/default.nix @@ -1,4 +1,4 @@ { inputs }: { mkFleet = import ./mkFleet.nix; -} \ No newline at end of file +} diff --git a/lib/mkFleet.nix b/lib/mkFleet.nix index 4de4d49..94a034c 100644 --- a/lib/mkFleet.nix +++ b/lib/mkFleet.nix @@ -5,7 +5,7 @@ fleet ? null, hwTypes ? null, }: - import ../fleet/default.nix { - inherit inputs; - inherit fleet hwTypes; - } \ No newline at end of file +import ../fleet/default.nix { + inherit inputs; + inherit fleet hwTypes; +} diff --git a/sw/gc.nix b/sw/gc.nix index 78658cf..e679c02 100644 --- a/sw/gc.nix +++ b/sw/gc.nix @@ -55,4 +55,4 @@ # Optimize storage nix.optimise.automatic = config.athenix.system.gc.optimise; }; -} \ No newline at end of file +} -- 2.39.5