fix: Respect nvim user config option

This commit is contained in:
UGA Innovation Factory
2025-12-16 13:47:50 -05:00
committed by Hunter Halloran
parent 1c71bf099e
commit 3b0c147b3f
8 changed files with 321 additions and 159 deletions

View File

@@ -13,7 +13,22 @@
{
options.ugaif = {
forUser = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
description = ''
Convenience option to configure a host for a specific user.
Automatically adds the user to extraUsers and sets wslUser for WSL hosts.
Value should be a username from ugaif.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).";
};
filesystem = {
device = lib.mkOption {
type = lib.types.str;

View File

@@ -8,10 +8,25 @@
# Host Generator
# ============================================================================
# This file contains the logic to generate NixOS configurations for all hosts
# defined in inventory.nix. It handles:
# 1. Common module imports (boot, users, software).
# 2. Host-specific overrides (filesystem, enabled users).
# 3. External flake integration for system overrides.
# defined in inventory.nix. It supports both hostname-based and count-based
# configurations with flexible type associations.
#
# Inventory format:
# {
# "my-hostname" = {
# type = "nix-desktop"; # Host type module to use
# system = "x86_64-linux"; # Optional
# # ... any ugaif.* options or device-specific config
# };
#
# "lab-prefix" = {
# type = "nix-laptop";
# count = 5; # Generates lab-prefix1, lab-prefix2, ... lab-prefix5
# devices = {
# "machine-1" = { ... }; # Override for lab-prefix1
# };
# };
# }
let
nixpkgs = inputs.nixpkgs;
@@ -21,11 +36,11 @@ let
{
hostName,
system ? "x86_64-linux",
extraModules ? [ ],
hostType,
configOverrides ? { },
}:
let
# Load users.nix to find external user flakes
# We use legacyPackages to evaluate the simple data structure of users.nix
pkgs = nixpkgs.legacyPackages.${system};
usersData = import ../users.nix { inherit pkgs; };
accounts = usersData.ugaif.users.accounts or { };
@@ -39,111 +54,177 @@ let
{ }
) accounts;
# Load the host type module
typeFile = ./types + "/${hostType}.nix";
typeModule =
if builtins.pathExists typeFile then
import typeFile { inherit inputs; }
else
throw "Host type '${hostType}' not found in hosts/types/";
# External flake override if specified
externalFlakeModule =
if configOverrides ? flakeUrl then
(builtins.getFlake configOverrides.flakeUrl).nixosModules.default
else
{ };
# Config override module - translate special keys to ugaif options
overrideModule =
{ ... }:
let
cleanConfig = lib.removeAttrs configOverrides [
"type"
"count"
"devices"
"overrides"
"defaultCount"
"extraUsers"
"flakeUrl"
"hostname"
"buildMethods"
"wslUser"
];
specialConfig = lib.mkMerge [
(lib.optionalAttrs (configOverrides ? extraUsers) {
ugaif.users.enabledUsers = configOverrides.extraUsers;
})
(lib.optionalAttrs (configOverrides ? buildMethods) {
ugaif.host.buildMethods = configOverrides.buildMethods;
})
(lib.optionalAttrs (configOverrides ? wslUser) {
ugaif.host.wsl.user = configOverrides.wslUser;
})
];
in
{
config = lib.mkMerge [
cleanConfig
specialConfig
];
};
allModules =
userFlakeModules
++ extraModules
++ [
typeModule
overrideModule
{ networking.hostName = hostName; }
];
]
++ lib.optional (configOverrides ? flakeUrl) externalFlakeModule;
in
{
system = lib.nixosSystem {
inherit system;
specialArgs = { inherit inputs; };
modules = allModules;
};
modules = allModules;
};
# Function to generate a set of hosts based on inventory count and overrides
mkHostGroup =
{
prefix,
count,
system ? "x86_64-linux",
extraModules ? [ ],
deviceOverrides ? { },
}:
lib.listToAttrs (
lib.concatMap (
i:
let
defaultName = "${prefix}${toString i}";
devConf = deviceOverrides.${toString i} or { };
hasOverride = builtins.hasAttr (toString i) deviceOverrides;
hostName =
if hasOverride && (builtins.hasAttr "hostname" devConf) then devConf.hostname else defaultName;
# Extract flakeUrl if it exists
externalFlake =
if hasOverride && (builtins.hasAttr "flakeUrl" devConf) then
builtins.getFlake devConf.flakeUrl
else
null;
# Module from external flake
externalModule = if externalFlake != null then externalFlake.nixosModules.default else { };
# Config override module (filesystem, users)
overrideModule =
{ ... }:
let
# Extract device-specific config, removing special keys that need custom handling
baseConfig = lib.removeAttrs devConf [
"extraUsers"
"flakeUrl"
"hostname"
"buildMethods"
"wslUser"
];
# Handle special keys that map to specific ugaif options
specialConfig = lib.mkMerge [
(lib.optionalAttrs (devConf ? extraUsers) { ugaif.users.enabledUsers = devConf.extraUsers; })
(lib.optionalAttrs (devConf ? buildMethods) { ugaif.host.buildMethods = devConf.buildMethods; })
(lib.optionalAttrs (devConf ? wslUser) { ugaif.host.wsl.user = devConf.wslUser; })
];
in
lib.mkIf hasOverride (lib.recursiveUpdate baseConfig specialConfig);
config = mkHost {
hostName = hostName;
inherit system;
extraModules =
extraModules ++ [ overrideModule ] ++ (lib.optional (externalFlake != null) externalModule);
};
aliasNames = lib.optional (hostName != defaultName) hostName;
names = lib.unique ([ defaultName ] ++ aliasNames);
in
lib.map (name: {
inherit name;
value = config;
}) names
) (lib.range 1 count)
);
# Generate host groups based on the input hosts configuration
hostGroups = lib.mapAttrsToList (
type: config:
# Process inventory entries - top-level keys are always prefixes
processInventory = lib.mapAttrs (
prefix: config:
let
typeFile = ./types + "/${type}.nix";
modules =
if builtins.pathExists typeFile then
[ (import typeFile { inherit inputs; }) ]
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
throw "Host type '${type}' not found in hosts/types/";
"${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
usePrefix = deviceConfig.ugaif.host.useHostPrefix or true;
hostName = mkHostName prefix deviceKey usePrefix;
# Merge: base config -> overrides -> device-specific config
mergedConfig = lib.recursiveUpdate (lib.recursiveUpdate baseConfig overrides) deviceConfig;
in
{
name = hostName;
value = mkHost {
inherit hostName system hostType;
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
mkHostGroup {
prefix = type;
inherit (config) count;
extraModules = modules;
deviceOverrides = config.devices or { };
}
lib.recursiveUpdate deviceHosts countHosts
) hosts;
allHosts = lib.foldl' lib.recursiveUpdate { } hostGroups;
# Flatten the nested structure
allHosts = lib.foldl' lib.recursiveUpdate { } (lib.attrValues processInventory);
in
{
nixosConfigurations = lib.mapAttrs (n: v: v.system) allHosts;

View File

@@ -19,7 +19,8 @@
config = {
wsl.enable = true;
wsl.defaultUser = config.ugaif.host.wsl.user;
wsl.defaultUser =
if config.ugaif.forUser != null then config.ugaif.forUser else config.ugaif.host.wsl.user;
# Enable the headless software profile
ugaif.sw.enable = lib.mkDefault true;

View File

@@ -2,6 +2,7 @@
pkgs,
config,
lib,
inputs,
...
}:
@@ -100,7 +101,8 @@ in
ugaif.users.enabledUsers = [
"root"
"engr-ugaif"
];
]
++ lib.optional (config.ugaif.forUser != null) config.ugaif.forUser;
# Generate NixOS users
users.users =
@@ -131,6 +133,7 @@ in
useUserPackages = true;
extraSpecialArgs = {
osConfig = config;
inherit inputs;
};
users =
@@ -146,8 +149,9 @@ in
isExternal = user.flakeUrl != "";
# Common imports based on flags
commonImports =
lib.optional user.useZshTheme ../sw/theme.nix ++ lib.optional user.useNvimPlugins ../sw/nvim.nix;
commonImports = lib.optional user.useZshTheme ../sw/theme.nix ++ [
(import ../sw/nvim.nix { inherit user; })
];
in
if isExternal then
{