Initial commit

This commit is contained in:
kirbara 2025-12-01 13:23:24 +07:00
commit cfcc57a8bd
Signed by: exp
GPG key ID: D7E63AD0019E75D9
353 changed files with 18756 additions and 0 deletions

View file

@ -0,0 +1,205 @@
{
inputs,
lib,
config,
...
}:
let
inherit (config) den;
hostsOption = lib.mkOption {
description = "den hosts definition";
default = { };
type = lib.types.attrsOf systemType;
};
systemType = lib.types.submodule (
{ name, ... }:
{
freeformType = lib.types.attrsOf (hostType name);
}
);
hostType =
system:
lib.types.submodule (
{ name, config, ... }:
{
freeformType = lib.types.attrsOf lib.types.anything;
options = {
name = strOpt "host configuration name" name;
hostName = strOpt "Network hostname" config.name;
system = strOpt "platform system" system;
class = strOpt "os-configuration nix class for host" (
if lib.hasSuffix "darwin" config.system then "darwin" else "nixos"
);
aspect = strOpt "main aspect name of <class>" config.name;
description = strOpt "host description" "${config.class}.${config.hostName}@${config.system}";
users = lib.mkOption {
description = "user accounts";
default = { };
type = lib.types.attrsOf userType;
};
instantiate = lib.mkOption {
description = ''
Function used to instantiate the OS configuration.
Depending on class, defaults to:
`darwin`: inputs.darwin.lib.darwinSystem
`nixos`: inputs.nixpkgs.lib.nixosSystem
`systemManager`: inputs.system-manager.lib.makeSystemConfig
Set explicitly if you need:
- a custom input name, eg, nixos-unstable.
- adding specialArgs when absolutely required.
'';
example = lib.literalExpression "inputs.nixpkgs.lib.nixosSystem";
type = lib.types.unspecified;
default =
{
nixos = inputs.nixpkgs.lib.nixosSystem;
darwin = inputs.darwin.lib.darwinSystem;
systemManager = inputs.system-manager.lib.makeSystemConfig;
}
.${config.class};
};
intoAttr = lib.mkOption {
description = ''
Flake attr where to add the named result of this configuration.
flake.<intoAttr>.<name>
Depending on class, defaults to:
`darwin`: darwinConfigurations
`nixos`: nixosConfigurations
`systemManager`: systemConfigs
'';
example = lib.literalExpression ''"nixosConfigurations"'';
type = lib.types.str;
default =
{
nixos = "nixosConfigurations";
darwin = "darwinConfigurations";
systemManager = "systemConfigs";
}
.${config.class};
};
mainModule = lib.mkOption {
internal = true;
visible = false;
readOnly = true;
type = lib.types.deferredModule;
default = mainModule config "OS" "host";
};
};
}
);
userType = lib.types.submodule (
{ name, config, ... }:
{
freeformType = lib.types.attrsOf lib.types.anything;
options = {
name = strOpt "user configuration name" name;
userName = strOpt "user account name" config.name;
class = strOpt "home management nix class" "homeManager";
aspect = strOpt "main aspect name" config.name;
};
}
);
strOpt =
description: default:
lib.mkOption {
type = lib.types.str;
inherit description default;
};
homesOption = lib.mkOption {
description = "den standalone home-manager configurations";
default = { };
type = lib.types.attrsOf homeSystemType;
};
homeSystemType = lib.types.submodule (
{ name, ... }:
{
freeformType = lib.types.attrsOf (homeType name);
}
);
homeType =
system:
lib.types.submodule (
{ name, config, ... }:
{
freeformType = lib.types.attrsOf lib.types.anything;
options = {
name = strOpt "home configuration name" name;
userName = strOpt "user account name" config.name;
system = strOpt "platform system" system;
class = strOpt "home management nix class" "homeManager";
aspect = strOpt "main aspect name" config.name;
description = strOpt "home description" "home.${config.userName}@${config.system}";
instantiate = lib.mkOption {
description = ''
Function used to instantiate the home configuration.
Depending on class, defaults to:
`homeManager`: inputs.home-manager.lib.homeManagerConfiguration
Set explicitly if you need:
- a custom input name, eg, home-manager-unstable.
- adding extraSpecialArgs when absolutely required.
'';
example = lib.literalExpression "inputs.home-manager.lib.homeManagerConfiguration";
type = lib.types.unspecified;
default =
{
homeManager = inputs.home-manager.lib.homeManagerConfiguration;
}
.${config.class};
};
intoAttr = lib.mkOption {
description = ''
Flake attr where to add the named result of this configuration.
flake.<intoAttr>.<name>
Depending on class, defaults to:
`homeManager`: homeConfigurations
'';
example = lib.literalExpression ''"homeConfigurations"'';
type = lib.types.str;
default =
{
homeManager = "homeConfigurations";
}
.${config.class};
};
mainModule = lib.mkOption {
internal = true;
visible = false;
readOnly = true;
type = lib.types.deferredModule;
default = mainModule config "HM" "home";
};
};
}
);
mainModule =
from: intent: name:
let
asp = den.aspects.${from.aspect};
ctx = {
${intent} = asp;
${name} = from;
};
mod = (asp ctx).resolve { inherit (from) class; };
in
mod;
in
{
inherit hostsOption homesOption;
}

View file

@ -0,0 +1,8 @@
# creates den.default aspect
{ lib, den, ... }:
{
config.den.default.__functor = den.lib.parametric.atLeast;
options.den.default = lib.mkOption {
type = den.lib.aspects.types.aspectSubmodule;
};
}

View file

@ -0,0 +1,34 @@
# create aspect dependencies from hosts/users
{
lib,
den,
...
}:
let
inherit (den.lib) parametric;
makeAspect = from: {
${from.aspect} = {
${from.class} = { };
includes = [ den.default ];
__functor = parametric.atLeast;
};
};
hosts = map builtins.attrValues (builtins.attrValues den.hosts);
homes = map builtins.attrValues (builtins.attrValues den.homes);
aspectClass = from: { inherit (from) aspect class; };
deps = lib.pipe hosts [
(lib.flatten)
(map (h: builtins.attrValues h.users))
(users: users ++ hosts ++ homes)
(lib.flatten)
(map aspectClass)
(lib.unique)
(map makeAspect)
];
in
{
den.aspects = lib.mkMerge deps;
}

View file

@ -0,0 +1,97 @@
{
den,
lib,
...
}:
let
inherit (den.lib)
owned
statics
parametric
;
inherit (den.lib.take) exactly;
dependencies = [
(exactly osDependencies)
(exactly hmUserDependencies)
(exactly hmStandaloneDependencies)
];
osDependencies =
{ OS, host }:
{
includes = [
(owned den.default)
(statics den.default)
(owned OS)
(statics OS)
{
includes =
let
users = builtins.attrValues host.users;
contrib = osUserDependencies { inherit OS host; };
in
map contrib users;
}
];
};
osUserDependencies =
{ OS, host }:
user:
let
USR = den.aspects.${user.aspect};
in
{
includes = [
(owned USR)
(statics USR)
(USR { inherit OS host user; })
];
};
# from OS home-managed integration.
hmUserDependencies =
{
OS-HM,
host,
user,
}:
let
inherit (OS-HM) OS HM;
in
{
includes = [
(owned den.default)
(statics den.default)
(owned HM)
(statics HM)
(owned OS)
(statics OS)
(parametric {
inherit
OS
HM
user
host
;
} OS)
];
};
hmStandaloneDependencies =
{ HM, home }:
{
includes = [
(owned den.default)
(statics den.default)
(owned HM)
(statics HM)
];
};
in
{
den.default.includes = dependencies;
}

View file

@ -0,0 +1,17 @@
{ lib, config, ... }:
{
options.den = lib.mkOption {
type = lib.types.submodule {
imports = [
(lib.mkAliasOptionModule [ "_" ] [ "provides" ])
];
options.provides = lib.mkOption {
default = { };
description = "Batteries Included - re-usable high-level aspects";
type = lib.types.submodule {
freeformType = lib.types.attrsOf config.den.lib.aspects.types.providerType;
};
};
};
};
}

View file

@ -0,0 +1,55 @@
{ lib, den, ... }:
let
description = ''
Defines a user at OS and Home levels.
Works in NixOS/Darwin and standalone Home-Manager
## Usage
# for NixOS/Darwin
den.aspects.my-user.includes = [ den._.define-user ]
# for standalone home-manager
den.aspects.my-home.includes = [ den._.define-user ]
or globally (automatically applied depending on context):
den.default.includes = [ den._.define-user ]
'';
homeDir =
host: user:
if lib.hasSuffix "darwin" host.system then "/Users/${user.userName}" else "/home/${user.userName}";
userContext =
{ host, user, ... }:
{
nixos.users.users.${user.userName}.isNormalUser = true;
darwin.users.users.${user.userName} = {
name = user.userName;
home = homeDir host user;
};
homeManager = {
home.username = user.userName;
home.homeDirectory = homeDir host user;
};
};
hmContext =
{ home, ... }:
userContext {
host.system = home.system;
user.userName = home.userName;
};
in
{
den.provides.define-user = {
inherit description;
includes = [
userContext
hmContext
];
__functor = den.lib.parametric.atLeast;
};
}

View file

@ -0,0 +1,76 @@
{
inputs,
lib,
den,
...
}:
let
description = ''
integrates home-manager into nixos/darwin OS classes.
usage:
for using home-manager in just a particular host:
den.aspects.my-laptop.includes = [ den._.home-manager ];
for enabling home-manager by default on all hosts:
den.default.includes = [ den._.home-manager ];
Does nothing for hosts that have no users with `homeManager` class.
Expects `inputs.home-manager` to exist. If `<host>.hm-module` exists
it is the home-manager.{nixos/darwin}Modules.home-manager.
For each user resolves den.aspects.''${user.aspect} and imports its homeManager class module.
'';
homeManager =
{ OS, host }:
{ class, aspect-chain }:
let
hmClass = "homeManager";
hmUsers = builtins.filter (u: u.class == hmClass) (lib.attrValues host.users);
hmUserModule =
user:
let
ctx = {
inherit aspect-chain;
class = hmClass;
};
HM = den.aspects.${user.aspect};
aspect = HM {
inherit host user;
OS-HM = { inherit OS HM; };
};
module = aspect.resolve ctx;
in
module;
users = map (user: {
name = user.userName;
value.imports = [ (hmUserModule user) ];
}) hmUsers;
hmModule = host.hm-module or inputs.home-manager."${class}Modules".home-manager;
aspect.${class} = {
imports = [ hmModule ];
home-manager.users = lib.listToAttrs users;
};
supportedOS = builtins.elem class [
"nixos"
"darwin"
];
enabled = supportedOS && builtins.length hmUsers > 0;
in
if enabled then aspect else { };
in
{
den.provides.home-manager = {
inherit description;
__functor = _: den.lib.take.exactly homeManager;
};
}

View file

@ -0,0 +1,77 @@
{
inputs,
den,
...
}:
{
den.provides.import-tree.description = ''
Recursively imports non-dendritic .nix files depending on their Nix configuration `class`.
This can be used to help migrating from huge existing setups.
```
# this is at <repo>/modules/non-dendritic.nix
den.aspects.my-laptop.includes = [
(den._.import-tree._.host ../non-dendritic)
]
```
With following structure, it will automatically load modules depending on their class.
```
<repo>/
modules/
non-dendritic.nix # configures this aspect
non-dendritic/ # name is just an example here
hosts/
my-laptop/
_nixos/ # a directory for `nixos` class
auto-generated-hardware.nix # any nixos module
_darwin/
foo.nix
_homeManager/
me.nix
```
## Requirements
- inputs.import-tree
## Usage
this aspect can be included explicitly on any aspect:
# example: will import ./disko/_nixos files automatically.
den.aspects.my-disko.includes = [ (den._.import-tree ./disko/) ];
or it can be default imported per host/user/home:
# load from ./hosts/<host>/_nixos
den.default.includes = [ (den._.import-tree._.host ./hosts) ];
# load from ./users/<user>/{_homeManager, _nixos}
den.default.includes = [ (den._.import-tree._.user ./users) ];
# load from ./homes/<home>/_homeManager
den.default.includes = [ (den._.import-tree._.home ./homes) ];
you are also free to create your own auto-imports layout following the implementation of these.
'';
den._.import-tree.__functor =
_: root:
# deadnix: skip
{ class, aspect-chain }:
let
path = "${toString root}/_${class}";
aspect.${class}.imports = [ (inputs.import-tree path) ];
in
if builtins.pathExists path then aspect else { };
den._.import-tree.provides = {
host = root: { host, ... }: den._.import-tree "${toString root}/${host.name}";
home = root: { home, ... }: den._.import-tree "${toString root}/${home.name}";
user = root: { user, ... }: den._.import-tree "${toString root}/${user.name}";
};
}

View file

@ -0,0 +1,37 @@
{ lib, ... }:
let
description = ''
Sets user as *primary*.
On NixOS adds wheel and networkmanager groups.
On Darwin sets user as system.primaryUser
On WSL sets wsl.defaultUser if host has an `wsl` attribute.
## Usage
den.aspects.my-user.includes = [ den._.primary-user ];
'';
userToHostContext =
{ user, host, ... }:
let
on-wsl.nixos.wsl.defaultUser = user.userName;
in
{
inherit description;
includes = lib.optionals (host ? wsl) [ on-wsl ];
darwin.system.primaryUser = user.userName;
nixos.users.users.${user.userName} = {
isNormalUser = true;
extraGroups = [
"wheel"
"networkmanager"
];
};
};
in
{
den.provides.primary-user = userToHostContext;
}

View file

@ -0,0 +1,22 @@
{ lib, ... }:
{
den.provides.unfree.description = ''
A class generic aspect that enables unfree packages by name.
Works for any class (nixos/darwin/homeManager,etc) on any host/user/home context.
## Usage
den.aspects.my-laptop.includes = [ (den._.unfree [ "code" ]) ];
It will dynamically provide a module for each class when accessed.
'';
den.provides.unfree.__functor =
_self: allowed-names:
# deadnix: allow
{ class, aspect-chain }:
{
${class}.nixpkgs.config.allowUnfreePredicate = pkg: builtins.elem (lib.getName pkg) allowed-names;
};
}

View file

@ -0,0 +1,41 @@
{ den, ... }:
let
description = ''
Sets a user default shell, enables the shell at OS and Home level.
Usage:
den.aspects.vic.includes = [
# will always love red snappers.
(den._.user-shell "fish")
];
'';
userShell =
shell: user:
let
nixos =
{ pkgs, ... }:
{
programs.${shell}.enable = true;
users.users.${user.userName}.shell = pkgs.${shell};
};
darwin = nixos;
homeManager.programs.${shell}.enable = true;
in
{
inherit nixos darwin homeManager;
};
in
{
den.provides.user-shell = shell: {
inherit description;
__functor = den.lib.parametric.atLeast;
includes = [
({ user, ... }: userShell shell user)
({ home, ... }: userShell shell home)
];
};
}

View file

@ -0,0 +1,58 @@
{
config,
lib,
withSystem,
inputs,
...
}:
let
build =
builder: cfg:
let
items = map builtins.attrValues (builtins.attrValues cfg);
buildItem = item: {
inherit (item) name intoAttr;
value = builder item;
};
in
map buildItem (lib.flatten items);
osConfiguration =
host:
host.instantiate {
specialArgs = {
inherit inputs;
};
modules = [
host.mainModule
{ nixpkgs.hostPlatform = lib.mkDefault host.system; }
];
};
homeConfiguration =
home:
withSystem home.system (
{ pkgs, ... }:
home.instantiate {
inherit pkgs;
extraSpecialArgs = { inherit inputs; };
modules = [ home.mainModule ];
}
);
cfgs = (build osConfiguration config.den.hosts) ++ (build homeConfiguration config.den.homes);
outputs =
acc: item:
acc
// {
${item.intoAttr} = (acc.${item.intoAttr} or { }) // {
${item.name} = item.value;
};
};
in
{
flake = lib.foldl outputs { } cfgs;
}

14
flake/den/modules/lib.nix Normal file
View file

@ -0,0 +1,14 @@
{
lib,
inputs,
config,
...
}:
{
config.den.lib = inputs.den.lib { inherit inputs lib config; };
options.den.lib = lib.mkOption {
internal = true;
visible = false;
type = lib.types.attrsOf lib.types.raw;
};
}

View file

@ -0,0 +1,13 @@
{
inputs,
lib,
config,
...
}:
let
types = import ./_types.nix { inherit inputs lib config; };
in
{
options.den.hosts = types.hostsOption;
options.den.homes = types.homesOption;
}

View file

@ -0,0 +1,10 @@
{
inputs,
config,
lib,
...
}:
{
config._module.args.den = config.den;
imports = [ ((inputs.flake-aspects.lib lib).new-scope "den") ];
}