Modular, context-aware and aspect-oriented dendritic Nix configurations. Discussions: https://oeiuwq.zulipchat.com/join/nqp26cd4kngon6mo3ncgnuap/ den.oeiuwq.com
configurations den dendritic nix aspect oriented

Directional dependencies contexts are now `{ userToHost }` and `{ hostToUser }` (#54)

Instead of previous: `{ fromUser, toHost }` and `{ fromHost, toUser }`.

The reason is detailed at #47.

Closes #47.

authored by oeiuwq.com and committed by

GitHub 4f5653b0 029568fd

+77 -37
+33 -2
checkmate/tests/aspect-functor.nix
··· 19 19 } 20 20 ) 21 21 ( 22 - { host, user }: 22 + { host, user, ... }: 23 23 { 24 24 nixos.host-user = [ 25 25 host 26 26 user 27 + ]; 28 + } 29 + ) 30 + ( 31 + { userToHost, ... }: 32 + { 33 + nixos.user-to-host = [ 34 + userToHost.host 35 + userToHost.user 27 36 ]; 28 37 } 29 38 ) ··· 136 145 1 137 146 2 138 147 ]; 139 - } # host user 148 + } 140 149 { nixos.user = 2; } 141 150 { nixos.user-only = false; } 151 + { nixos.any = 10; } 152 + ]; 153 + }; 154 + }; 155 + 156 + flake.tests."test functor applied with userToHost" = { 157 + expr = ( 158 + aspect-example { 159 + userToHost = { 160 + user = 2; 161 + host = 1; 162 + }; 163 + } 164 + ); 165 + expected = { 166 + includes = [ 167 + { 168 + nixos.user-to-host = [ 169 + 1 170 + 2 171 + ]; 172 + } 142 173 { nixos.any = 10; } 143 174 ]; 144 175 };
+10 -8
modules/aspects/dependencies.nix
··· 22 22 ({ user, ... }: statics den.aspects.${user.aspect}) 23 23 24 24 # user-to-host context 25 - ({ fromUser, toHost }: owned toHost.class den.aspects.${fromUser.aspect}) 25 + ({ userToHost, ... }: owned userToHost.host.class den.aspects.${userToHost.user.aspect}) 26 26 # host-to-user context 27 - ({ fromHost, toUser }: owned toUser.class den.aspects.${fromHost.aspect}) 27 + ({ hostToUser, ... }: owned hostToUser.user.class den.aspects.${hostToUser.host.aspect}) 28 28 29 - # { host } => [ { fromUser, toHost } ] 29 + # { host } => [ { userToHost } ] 30 30 (hostIncludesFromUsers) 31 31 32 - # { user, host } => { fromHost, toUser } 32 + # { user, host } => { hostToUser } 33 33 (userIncludesFromHost) 34 34 ]; 35 35 ··· 40 40 let 41 41 users = builtins.attrValues host.users; 42 42 context = user: { 43 - fromUser = user; 44 - toHost = host; 43 + userToHost = { 44 + inherit user host; 45 + }; 45 46 }; 46 47 contrib = user: den.aspects.${user.aspect} (context user); 47 48 in ··· 53 54 { 54 55 includes = den.aspects.${host.aspect}.includes; 55 56 __functor = den.lib.parametric { 56 - fromHost = host; 57 - toUser = user; 57 + hostToUser = { 58 + inherit host user; 59 + }; 58 60 }; 59 61 }; 60 62
+8 -5
modules/aspects/provides/define-user.nix
··· 23 23 if lib.hasSuffix "darwin" host.system then "/Users/${user.userName}" else "/home/${user.userName}"; 24 24 25 25 userToHostContext = 26 - { fromUser, toHost }: 26 + { userToHost, ... }: 27 + let 28 + inherit (userToHost) host user; 29 + in 27 30 { 28 - nixos.users.users.${fromUser.userName}.isNormalUser = true; 29 - darwin.users.users.${fromUser.userName} = { 30 - name = fromUser.userName; 31 - home = homeDir toHost fromUser; 31 + nixos.users.users.${user.userName}.isNormalUser = true; 32 + darwin.users.users.${user.userName} = { 33 + name = user.userName; 34 + home = homeDir host user; 32 35 }; 33 36 }; 34 37
+6 -5
modules/aspects/provides/primary-user.nix
··· 14 14 ''; 15 15 16 16 userToHostContext = 17 - { fromUser, toHost }: 17 + { userToHost, ... }: 18 18 let 19 - on-wsl.nixos.wsl.defaultUser = fromUser.userName; 19 + inherit (userToHost) host user; 20 + on-wsl.nixos.wsl.defaultUser = user.userName; 20 21 in 21 22 { 22 23 inherit description; 23 - includes = lib.optionals (toHost ? wsl) [ on-wsl ]; 24 - darwin.system.primaryUser = fromUser.userName; 25 - nixos.users.users.${fromUser.userName} = { 24 + includes = lib.optionals (host ? wsl) [ on-wsl ]; 25 + darwin.system.primaryUser = user.userName; 26 + nixos.users.users.${user.userName} = { 26 27 isNormalUser = true; 27 28 extraGroups = [ 28 29 "wheel"
+3 -2
modules/aspects/provides/user-shell.nix
··· 21 21 22 22 userToHostContext = 23 23 { shell }: 24 - { fromUser, toHost }: 24 + { userToHost, ... }: 25 25 let 26 + inherit (userToHost) user; 26 27 nixos = 27 28 { pkgs, ... }: 28 29 { 29 30 programs.${shell}.enable = true; 30 - users.users.${fromUser.userName}.shell = pkgs.${shell}; 31 + users.users.${user.userName}.shell = pkgs.${shell}; 31 32 }; 32 33 darwin = nixos; 33 34 in
+7 -7
templates/default/modules/_example/aspects.nix
··· 33 33 34 34 # Example: installed on den.defaults for each user contribute into host. 35 35 one-hello-package-for-each-user = 36 - { fromUser, toHost }: 36 + { userToHost, ... }: 37 37 { 38 - ${toHost.class} = 38 + ${userToHost.host.class} = 39 39 { pkgs, ... }: 40 40 { 41 - users.users.${fromUser.userName}.packages = [ pkgs.hello ]; 41 + users.users.${userToHost.user.userName}.packages = [ pkgs.hello ]; 42 42 }; 43 43 }; 44 44 45 45 # Example: configuration that depends on both host and user. provides to the host. 46 46 user-to-host-conditional = 47 - { fromUser, toHost }: 48 - if fromUser.userName == "alice" && !lib.hasSuffix "darwin" toHost.system then 47 + { userToHost, ... }: 48 + if userToHost.user.userName == "alice" && !lib.hasSuffix "darwin" userToHost.host.system then 49 49 { 50 50 nixos.programs.tmux.enable = true; 51 51 } ··· 54 54 55 55 # Example: configuration that depends on both host and user. provides to the host. 56 56 host-to-user-conditional = 57 - { fromHost, toUser }: 58 - if toUser.userName == "alice" && !lib.hasSuffix "darwin" fromHost.system then 57 + { hostToUser, ... }: 58 + if hostToUser.user.userName == "alice" && !lib.hasSuffix "darwin" hostToUser.host.system then 59 59 { 60 60 homeManager.programs.git.enable = true; 61 61 }
+2 -2
templates/default/modules/_profile/hosts/bones/common-user-env.nix
··· 4 4 # private aspects can be let-bindings 5 5 # more re-usable ones are better defined inside the `pro` namespace. 6 6 host-contrib-to-user = 7 - { fromHost, toUser }: 8 - if fromHost.name == "bones" || toUser.name == "fido" then 7 + { hostToUser, ... }: 8 + if hostToUser.host.name == "bones" || hostToUser.user.name == "fido" then 9 9 { 10 10 homeManager.programs.vim.enable = true; 11 11 }
+6 -5
templates/default/modules/_profile/profiles/single-user-is-admin.nix
··· 3 3 4 4 # When a host includes *ONLY* one user, make that user the admin. 5 5 pro.single-user-is-admin = 6 - { fromUser, toHost }@context: 6 + { userToHost, ... }@context: 7 7 let 8 - single = 1 == builtins.length (builtins.attrValues toHost.users); 9 - exists = single && builtins.hasAttr fromUser.name toHost.users; 8 + inherit (userToHost) user host; 9 + single = 1 == builtins.length (builtins.attrValues host.users); 10 + exists = single && builtins.hasAttr user.name host.users; 10 11 admin = lib.optionals exists [ den._.primary-user ]; 11 - define = [ den._.define-user ]; 12 12 in 13 13 { 14 - includes = map (f: f context) (define ++ admin); 14 + __functor = den.lib.parametric context; 15 + includes = [ den._.define-user ] ++ admin; 15 16 }; 16 17 }
+2 -1
templates/default/modules/_profile/users/fido/common-host-env.nix
··· 3 3 { pro, ... }: 4 4 let 5 5 fido-at-host = 6 - { fromUser, toHost }: if fromUser.name != "fido" then { } else pro.fido._.${toHost.name}; 6 + { userToHost, ... }: 7 + if userToHost.user.name != "fido" then { } else pro.fido._.${userToHost.host.name}; 7 8 in 8 9 { 9 10 den.default.includes = [