initial commit

This commit is contained in:
gwg313 2026-04-15 18:26:05 -04:00
commit ebc1be5217
Signed by: gwg313
GPG key ID: 60FF63B4826B7400
143 changed files with 7721 additions and 0 deletions

View file

@ -0,0 +1,112 @@
{
lib,
inputs,
config,
...
}:
let
inherit (lib)
concatMapAttrs
mapAttrs'
nameValuePair
flatten
unique
;
hmLib = inputs.home-manager.lib;
allRoles = config.dendritic.roles;
allFeatures = config.dendritic.features;
resolveRoles =
let
go =
seen: roleName:
if builtins.elem roleName seen then
throw "Circular dendritic role reference detected: ${
builtins.concatStringsSep " -> " (seen ++ [ roleName ])
}"
else
let
role = allRoles.${roleName} or null;
in
if role == null then
throw "Unknown dendritic role: ${roleName}"
else
[ roleName ] ++ flatten (map (go (seen ++ [ roleName ])) role.roles);
in
roleNames: unique (flatten (map (go [ ]) roleNames));
resolveFeatures =
let
go =
seen: featureName:
if builtins.elem featureName seen then
throw "Circular dendritic feature reference detected: ${
builtins.concatStringsSep " -> " (seen ++ [ featureName ])
}"
else
let
feature = allFeatures.${featureName} or null;
in
if feature == null then
throw "Unknown dendritic feature: ${featureName}"
else
[ featureName ] ++ flatten (map (go (seen ++ [ featureName ])) feature.features);
in
featureNames: unique (flatten (map (go [ ]) featureNames));
mkHomeConfigurations = concatMapAttrs (
username: user:
mapAttrs' (
hostName: home:
let
resolvedRoleNames = resolveRoles home.roles;
roleFeatureNames = flatten (map (roleName: allRoles.${roleName}.features) resolvedRoleNames);
resolvedFeatureNames = resolveFeatures roleFeatureNames;
featureHomeModules = flatten (
map (featureName: allFeatures.${featureName}.homeModules) resolvedFeatureNames
);
in
nameValuePair "${username}@${hostName}" (
hmLib.homeManagerConfiguration {
pkgs = import inputs.nixpkgs {
system = home.system;
};
modules = [
../inventory/current.nix
../inventory/features.nix
../inventory/hosts.nix
../inventory/users.nix
../../aspects/roles.nix
{
dendritic.current = {
inherit hostName;
primaryUser = user.username;
};
}
]
++ user.homeModules
++ featureHomeModules
++ home.extraModules
++ [
{
home.username = user.username;
home.homeDirectory = "/home/${user.username}";
home.stateVersion = home.stateVersion;
}
];
extraSpecialArgs = {
inherit inputs;
};
}
)
) user.homes
) config.dendritic.users;
in
{
config.flake.homeConfigurations = mkHomeConfigurations;
}

View file

@ -0,0 +1,162 @@
{
lib,
inputs,
config,
...
}:
let
inherit (lib)
mapAttrs
flatten
unique
optionals
optional
;
allRoles = config.dendritic.roles;
allFeatures = config.dendritic.features;
resolveRoles =
let
go =
seen: roleName:
if builtins.elem roleName seen then
throw "Circular dendritic role reference detected: ${
builtins.concatStringsSep " -> " (seen ++ [ roleName ])
}"
else
let
role = allRoles.${roleName} or null;
in
if role == null then
throw "Unknown dendritic role: ${roleName}"
else
[ roleName ] ++ flatten (map (go (seen ++ [ roleName ])) role.roles);
in
roleNames: unique (flatten (map (go [ ]) roleNames));
resolveFeatures =
let
go =
seen: featureName:
if builtins.elem featureName seen then
throw "Circular dendritic feature reference detected: ${
builtins.concatStringsSep " -> " (seen ++ [ featureName ])
}"
else
let
feature = allFeatures.${featureName} or null;
in
if feature == null then
throw "Unknown dendritic feature: ${featureName}"
else
[ featureName ] ++ flatten (map (go (seen ++ [ featureName ])) feature.features);
in
featureNames: unique (flatten (map (go [ ]) featureNames));
mkHost =
hostName: host:
let
username = if host.primaryUser == null then null else host.primaryUser;
user = if username == null then null else config.dendritic.users.${username};
userHome = if user == null then null else user.homes.${hostName} or null;
resolvedRoleNames = resolveRoles host.roles;
roleFeatureNames = flatten (map (roleName: allRoles.${roleName}.features) resolvedRoleNames);
resolvedFeatureNames = resolveFeatures roleFeatureNames;
featureNixosModules = flatten (
map (featureName: allFeatures.${featureName}.nixosModules) resolvedFeatureNames
);
featureHomeModules =
if userHome == null then
[ ]
else
let
resolvedHomeRoleNames = resolveRoles userHome.roles;
homeRoleFeatureNames = flatten (
map (roleName: allRoles.${roleName}.features) resolvedHomeRoleNames
);
resolvedHomeFeatureNames = resolveFeatures homeRoleFeatureNames;
embeddedHomeFeatureNames = builtins.filter (
featureName: allFeatures.${featureName}.includeInEmbeddedHomeManager
) resolvedHomeFeatureNames;
in
flatten (map (featureName: allFeatures.${featureName}.homeModules) embeddedHomeFeatureNames);
in
inputs.nixpkgs.lib.nixosSystem {
system = host.system;
specialArgs = {
inherit inputs;
};
modules = [
../inventory/current.nix
../inventory/features.nix
../inventory/hosts.nix
../inventory/users.nix
../../aspects/roles.nix
{
dendritic.current = {
inherit hostName;
primaryUser = username;
};
networking.hostName = hostName;
}
]
++ host.nixosModules
++ featureNixosModules
++ optional (username != null) {
users.users.${username} = {
isNormalUser = true;
extraGroups = host.primaryUserExtraGroups;
};
}
++ optionals (user != null) user.nixosModules
++ optional (userHome != null) inputs.home-manager.nixosModules.home-manager
++ optional (userHome != null) {
home-manager.useGlobalPkgs = true;
home-manager.useUserPackages = true;
home-manager.extraSpecialArgs = {
inherit inputs;
};
home-manager.sharedModules = [
../inventory/current.nix
../inventory/features.nix
../inventory/hosts.nix
../inventory/users.nix
../../aspects/roles.nix
];
home-manager.users.${username}.imports = [
{
dendritic.current = {
inherit hostName;
primaryUser = username;
};
}
]
++ user.homeModules
++ featureHomeModules
++ userHome.extraModules
++ [
{
home.username = user.username;
home.homeDirectory = "/home/${user.username}";
home.stateVersion = userHome.stateVersion;
}
];
};
};
in
{
config.flake.nixosConfigurations = mapAttrs mkHost config.dendritic.hosts;
}

View file

@ -0,0 +1,12 @@
{ lib, ... }:
{
options.dendritic.current = {
hostName = lib.mkOption {
type = lib.types.str;
};
primaryUser = lib.mkOption {
type = lib.types.str;
};
};
}

View file

@ -0,0 +1,36 @@
{ lib, ... }:
{
options.dendritic.features = lib.mkOption {
type = lib.types.attrsOf (
lib.types.submodule ({ name, ... }: {
options = {
name = lib.mkOption {
type = lib.types.str;
default = name;
};
features = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
};
nixosModules = lib.mkOption {
type = lib.types.listOf lib.types.deferredModule;
default = [ ];
};
homeModules = lib.mkOption {
type = lib.types.listOf lib.types.deferredModule;
default = [ ];
};
includeInEmbeddedHomeManager = lib.mkOption {
type = lib.types.bool;
default = true;
};
};
})
);
default = { };
};
}

View file

@ -0,0 +1,43 @@
{lib, ...}: {
options.dendritic.hosts = lib.mkOption {
type = lib.types.attrsOf (
lib.types.submodule ({name, ...}: {
options = {
name = lib.mkOption {
type = lib.types.str;
default = name;
};
system = lib.mkOption {
type = lib.types.str;
};
type = lib.mkOption {
type = lib.types.enum ["laptop" "desktop" "server"];
};
roles = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [];
};
primaryUser = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
};
primaryUserExtraGroups = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = ["wheel" "networkmanager"];
};
nixosModules = lib.mkOption {
type = lib.types.listOf lib.types.deferredModule;
default = [];
};
};
})
);
default = {};
};
}

View file

@ -0,0 +1,63 @@
{lib, ...}: {
options.dendritic.users = lib.mkOption {
type = lib.types.attrsOf (
lib.types.submodule ({name, ...}: {
options = {
name = lib.mkOption {
type = lib.types.str;
default = name;
};
username = lib.mkOption {
type = lib.types.str;
default = name;
};
nixosModules = lib.mkOption {
type = lib.types.listOf lib.types.deferredModule;
default = [];
};
homeModules = lib.mkOption {
type = lib.types.listOf lib.types.deferredModule;
default = [];
};
homes = lib.mkOption {
type = lib.types.attrsOf (
lib.types.submodule ({name, ...}: {
options = {
host = lib.mkOption {
type = lib.types.str;
default = name;
};
system = lib.mkOption {
type = lib.types.str;
};
stateVersion = lib.mkOption {
type = lib.types.str;
default = "25.05";
};
roles = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [];
};
extraModules = lib.mkOption {
type = lib.types.listOf lib.types.deferredModule;
default = [];
};
};
})
);
default = {};
};
};
})
);
default = {};
};
}