feat: Add templates for external configs

This commit is contained in:
UGA Innovation Factory
2025-12-16 16:09:08 -05:00
committed by Hunter Halloran
parent f658a4a5cc
commit 11edaada84
17 changed files with 1102 additions and 23 deletions

View File

@@ -38,18 +38,31 @@ let
system ? "x86_64-linux",
hostType,
configOverrides ? { },
externalModulePath ? null,
}:
let
# Load users.nix to find external user flakes
# Load users.nix to find external user modules
pkgs = nixpkgs.legacyPackages.${system};
usersData = import ../users.nix { inherit pkgs; };
accounts = usersData.ugaif.users or { };
# Extract flakeUrls and convert to modules
userFlakeModules = lib.mapAttrsToList (
# Extract external user NixOS modules (if they exist)
# External user modules can optionally provide a nixos.nix file for system-level config
userNixosModules = lib.mapAttrsToList (
name: user:
if (user ? flakeUrl && user.flakeUrl != "") then
(builtins.getFlake user.flakeUrl).nixosModules.default
if (user ? home && user.home != null) then
let
homePath =
if builtins.isAttrs user.home && user.home ? outPath then
user.home.outPath
else
user.home;
nixosModulePath = homePath + "/nixos.nix";
in
if (builtins.isPath homePath || (builtins.isString homePath && lib.hasPrefix "/" homePath)) && builtins.pathExists nixosModulePath then
import nixosModulePath { inherit inputs; }
else
{ }
else
{ }
) accounts;
@@ -69,6 +82,13 @@ let
else
{ };
# External module from fetchGit/fetchurl
externalPathModule =
if externalModulePath != null then
import externalModulePath { inherit inputs; }
else
{ };
# Config override module - translate special keys to ugaif options
overrideModule =
{ ... }:
@@ -108,13 +128,14 @@ let
};
allModules =
userFlakeModules
userNixosModules
++ [
typeModule
overrideModule
{ networking.hostName = hostName; }
]
++ lib.optional (configOverrides ? flakeUrl) externalFlakeModule;
++ lib.optional (configOverrides ? flakeUrl) externalFlakeModule
++ lib.optional (externalModulePath != null) externalPathModule;
in
{
system = lib.nixosSystem {
@@ -170,16 +191,52 @@ let
lib.mapAttrsToList (
deviceKey: deviceConfig:
let
# Merge: base config -> overrides -> device-specific config
mergedConfig = lib.recursiveUpdate (lib.recursiveUpdate baseConfig overrides) deviceConfig;
# Check if deviceConfig is a path/derivation (from fetchGit, fetchurl, etc.)
# fetchGit/fetchTarball return an attrset with outPath attribute
isExternalModule =
(builtins.isPath deviceConfig)
|| (builtins.isString deviceConfig && lib.hasPrefix "/" deviceConfig)
|| (lib.isDerivation deviceConfig)
|| (builtins.isAttrs deviceConfig && deviceConfig ? outPath);
# Extract the actual path from fetchGit/fetchTarball results
extractedPath =
if builtins.isAttrs deviceConfig && deviceConfig ? outPath then
deviceConfig.outPath
else
deviceConfig;
# If external module, we use base config + overrides as the config
# and pass the module path separately
actualConfig = if isExternalModule then (lib.recursiveUpdate baseConfig overrides) else deviceConfig;
# Merge: base config -> overrides -> device-specific config (only if not external module)
mergedConfig =
if isExternalModule then
actualConfig
else
lib.recursiveUpdate (lib.recursiveUpdate baseConfig overrides) deviceConfig;
# Check useHostPrefix from the merged config
usePrefix = mergedConfig.ugaif.host.useHostPrefix or true;
hostName = mkHostName prefix deviceKey usePrefix;
# If external module, also add a default.nix path for import
externalModulePath =
if isExternalModule then
if builtins.isPath extractedPath then
extractedPath + "/default.nix"
else if lib.isDerivation extractedPath then
extractedPath + "/default.nix"
else
extractedPath + "/default.nix"
else
null;
in
{
name = hostName;
value = mkHost {
inherit hostName system hostType;
inherit hostName system hostType externalModulePath;
configOverrides = mergedConfig;
};
}

View File

@@ -49,10 +49,16 @@ let
type = lib.types.listOf lib.types.path;
default = [ ];
};
flakeUrl = lib.mkOption {
type = lib.types.str;
default = "";
description = "URL of a flake to import Home Manager configuration from (e.g. github:user/dotfiles).";
home = lib.mkOption {
type = lib.types.nullOr (lib.types.oneOf [ lib.types.path lib.types.package lib.types.attrs ]);
default = null;
description = ''
External home-manager configuration. Can be:
- A path to a local module
- A fetchGit/fetchTarball result pointing to a repository
- An attribute set with home-manager configuration
Example: builtins.fetchGit { url = "https://github.com/user/dotfiles"; rev = "..."; }
'';
};
opensshKeys = lib.mkOption {
type = lib.types.listOf lib.types.str;
@@ -138,18 +144,37 @@ in
name: user:
{ ... }:
let
isExternal = user.flakeUrl != "";
# Check if user has external home configuration
hasExternalHome = user.home != null;
# Extract path from fetchGit/fetchTarball if needed
externalHomePath =
if hasExternalHome then
if builtins.isAttrs user.home && user.home ? outPath then
user.home.outPath
else
user.home
else
null;
# Import external module if it's a path
externalHomeModule =
if externalHomePath != null && (builtins.isPath externalHomePath || (builtins.isString externalHomePath && lib.hasPrefix "/" externalHomePath)) then
import (externalHomePath + "/home.nix") { inherit inputs; }
else if builtins.isAttrs user.home && !(user.home ? outPath) then
user.home # Direct attrset configuration
else
{ };
# Common imports based on flags
commonImports = lib.optional user.useZshTheme ../sw/theme.nix ++ [
(import ../sw/nvim.nix { inherit user; })
];
in
if isExternal then
if hasExternalHome then
{
# External users: Only apply requested system modules.
# The external flake is responsible for home.username, home.packages, etc.
imports = commonImports;
# External users: Merge external config with common imports
imports = commonImports ++ [ externalHomeModule ];
}
else
{