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
8
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat(batteries): mutual provider can configure generic users and hosts (#283)

authored by

Victor Borja and committed by
GitHub
ea654224 6168d78c

+106 -73
+2 -8
README.md
··· 42 42 43 43 ### [Community](https://den.oeiuwq.com/community/) 44 44 45 - 46 - 47 45 </div> 48 46 </td> 49 47 <td> ··· 120 118 121 119 Den `main` always requires flake-aspects `main`. Same for `latest` versions. 122 120 123 - 124 121 ```nix 125 122 # Bleeding edge Den 126 123 { ··· 132 129 `main` branch follows development, this is where PRs are merged, it is stable in the sense 133 130 that every PR checks CI. However, some PR might introduce changes that require you being 134 131 aware of what is happening in Den. For people tracking development, we have announcements 135 - tagged `heads-up` whenever this happens. 136 - 137 - 132 + tagged `heads-up` whenever this happens. 138 133 139 134 ```nix 140 - # Released Den - no longer gets updates until next release 135 + # Released Den - no longer gets updates until next release 141 136 { 142 137 inputs.den.url = "github:vic/den/latest"; 143 138 inputs.flake-aspects.url = "github:vic/flake-aspects/latest"; ··· 153 148 For other than main and latest versions, each release-notes documents the particular 154 149 flake-aspects version needed by Den. This is because flake-aspects can be used independently of Den, 155 150 and it does not follows Den version numbers because it is not as fast-paced as Den. 156 - 157 151 158 152 ## Code example (OS configuration domain) 159 153
+1 -1
docs/astro.config.mjs
··· 69 69 { label: 'From Flake to Den', slug: 'guides/from-flake-to-den' }, 70 70 { label: 'Declare Hosts & Users', slug: 'guides/declare-hosts' }, 71 71 { label: 'Configure Aspects', slug: 'guides/configure-aspects' }, 72 - { label: 'Host<->User Bidirectionality', slug: 'guides/bidirectional' }, 72 + { label: 'Host<->User Mutual Config', slug: 'guides/bidirectional' }, 73 73 { label: 'Custom Nix Classes', slug: 'guides/custom-classes' }, 74 74 { label: 'Homes Integration', slug: 'guides/home-manager' }, 75 75 { label: 'Use Batteries', slug: 'guides/batteries' },
+34 -24
docs/src/content/docs/guides/bidirectional.mdx
··· 1 1 --- 2 - title: Host<-> User Bidirectional Configurations 3 - description: How to configure bidirectional behavior in Den and how it differs from mutual-provider. 2 + title: Host<->User Mutual Configurations 3 + description: How to configure bidirectional behavior in Den. 4 4 --- 5 5 6 6 import { Aside } from '@astrojs/starlight/components'; ··· 12 12 [`mutual-provider.nix`](https://github.com/vic/den/blob/main/modules/aspects/provides/mutual-provider.nix) 13 13 </Aside> 14 14 15 + ## What Mutual Configurations Means 15 16 16 - ## Normal, non-bidirectional OS configuration 17 + __Bidirectionality__ means that not only a User contributes 18 + configuration to a Host, but **also** that a Host contributes 19 + configurations to a User. 20 + 21 + This is useful when you wish for a Host to provide a common 22 + _home environment_ for all its users, or for a User to require 23 + common _os environment_ everywhere it is defined. 24 + 25 + There are at least two (built-in) ways to achieve this in Den. 26 + The `den._.bidirectional` and `den._.mutual-provider`, the 27 + first one was extracted from den-core and the second started 28 + life as an **aspect routing** example, but made it to become 29 + proper battery itself and even safer to use. 30 + 31 + 32 + ## Default, *unidirectional* OS configuration 17 33 18 34 Den framework is built around **context pipeline** transformations. 19 35 In order to create a full OS configuration, everything starts with a host definition: ··· 57 73 the host itself and from each of its user. 58 74 59 75 60 - ## What Bidirectionality means 61 - 62 - __Bidirectionality__ means that not only a User contributes 63 - configuration to a Host, but **also** that a Host contributes 64 - configurations to a User. 65 - 66 - This is useful when the Host wishes to provide a 67 - commmon home environment for its users. 68 76 69 77 ## `den.provides.bidirectional` 70 78 ··· 132 140 133 141 An alternative to bidirectionality is [`den.provides.mutual-provider`](https://github.com/vic/den/blob/main/modules/aspects/provides/mutual-provider.nix). 134 142 135 - This battery is more explicit, since it requires an explicit `.provides.` relationship between users and hosts. 143 + This battery is safer, instead of using the host aspect directly, it requires you to define other named aspects under `.provides.` to create an explicit relationship between users and hosts. 136 144 137 145 ```nix 138 - # Host provides to a particular user 139 - den.aspects.igloo.provides.tux = { 140 - hjem = ...; 146 + den.default.includes = [ den._.mutual-provider ]; 147 + 148 + # user aspect provides to specific host or to all where it lives 149 + den.aspects.tux = { 150 + provides.igloo.nixos.programs.emacs.enable = true; 151 + provides.to-hosts = { host, ... }: { 152 + nixos.programs.nh.enable = host.name == "igloo"; 153 + }; 141 154 }; 142 155 143 - # User provides to a particular host 144 - den.aspects.tux.provides.igloo = { 145 - nixos = ...; 156 + # host aspect provides to specific user or to all its users 157 + den.aspects.igloo = { 158 + provides.alice.homeManager.programs.vim.enable = true; 159 + provides.to-users = { user, ... }: { 160 + homeManager.programs.helix.enable = user.name == "alice"; 161 + }; 146 162 }; 147 163 ``` 148 - 149 - To enable it for both users and hosts, include at default: 150 - 151 - ```nix 152 - den.default.includes = [ den._.mutual-provider ]; 153 - ```
+37 -30
modules/aspects/provides/mutual-provider.nix
··· 1 1 # See usage at: templates/example/modules/aspects/{defaults.nix,alice.nix,igloo.nix} 2 2 { den, ... }: 3 3 let 4 + inherit (den.lib) take parametric; 5 + 4 6 description = '' 5 - Allows specifically-chosen hosts and users to contribute configuration **to 6 - each other** through `provides`. 7 - 8 - This is not the same as the built-in bidirectionality: 9 - 10 - # contributes to ALL users of this host 11 - den.aspects.my-host.homeManager = { ... } 12 - 13 - # contributes to ALL hosts of where my-user exist 14 - den.aspects.my-user.nixos = { ... } 15 - 16 - The difference is that this allows you to wire bidirectionality between 17 - explictly-named hosts/users pairs (see the usage below). 7 + Allows hosts and users to contribute configuration **to each other** 8 + through `provides`. 18 9 19 10 This battery implements an aspect "routing" pattern. 20 11 12 + This is not the same as `den._.bidirectional` battery, but provides a 13 + **safer** alternative to `den._.bidirectional`. 14 + The reason is that this battery does not re-invoke the `host-aspect.includes`, 15 + instead it relies on you defining provides. 16 + 21 17 Unlike `den.default` which is `parametric.atLeast` we use 22 18 `parametric.fixedTo` here, which help us propagate an already computed 23 19 context to all includes. 24 20 25 21 This battery, when installed in a `parametric.atLeast` will just forward 26 - the same context. The `mutual` helper returns an static configuration 22 + the same context. The `find-mutual` helper returns an static configuration 27 23 which is ignored by parametric aspects, thus allowing non-existing 28 24 aspects to be just ignored. 29 25 30 - Be sure to read the Host Context section on: 31 - https://den.oeiuwq.com/explanation/context-pipeline 26 + Be sure to read diagrams for the Host context pipeline: 27 + https://den.oeiuwq.com/guides/bidirectional 32 28 33 29 ## Usage 34 30 35 31 den.hosts.x86_64-linux.igloo.users.tux = { }; 36 32 den.default.includes = [ den._.mutual-provider ]; 37 33 38 - A user providing config TO the host: 39 - 34 + # user aspect provides to specific host or to all where it lives 40 35 den.aspects.tux = { 41 - provides.igloo = { host, ... }: { 36 + provides.igloo.nixos.programs.emacs.enable = true; 37 + provides.to-hosts = { host, ... }: { 42 38 nixos.programs.nh.enable = host.name == "igloo"; 43 39 }; 44 40 }; 45 41 46 - A host providing config TO the user: 47 - 42 + # host aspect provides to specific user or to all its users 48 43 den.aspects.igloo = { 49 - provides.tux = { user, ... }: { 44 + provides.alice.homeManager.programs.vim.enable = true; 45 + provides.to-users = { user, ... }: { 50 46 homeManager.programs.helix.enable = user.name == "alice"; 51 47 }; 52 48 }; 53 49 ''; 54 50 55 - mutual = from: to: den.aspects.${from.aspect}._.${to.aspect} or { }; 56 - in 57 - { 58 - den.provides.mutual-provider = 51 + find-mutual = from: to: den.aspects.${from.aspect}._.${to.aspect} or { }; 52 + user-to-hosts = { host, user }: den.aspects.${user.aspect}._.to-hosts or { }; 53 + host-to-users = { host, user }: den.aspects.${host.aspect}._.to-users or { }; 54 + 55 + named-mutuals = 59 56 { host, user }@ctx: 60 - den.lib.parametric.fixedTo ctx { 61 - inherit description; 57 + parametric.fixedTo ctx { 62 58 includes = [ 63 - (mutual user host) 64 - (mutual host user) 59 + (find-mutual host user) 60 + (find-mutual user host) 65 61 ]; 66 62 }; 63 + 64 + in 65 + { 66 + den.provides.mutual-provider = parametric.exactly { 67 + inherit description; 68 + includes = [ 69 + named-mutuals 70 + host-to-users 71 + user-to-hosts 72 + ]; 73 + }; 67 74 }
+27 -9
templates/ci/modules/features/batteries/mutual-provider.nix
··· 7 7 { 8 8 den.hosts.x86_64-linux.igloo.users.tux = { }; 9 9 10 - den.default.nixos.system.stateVersion = "25.11"; 11 - den.default.homeManager.home.stateVersion = "25.11"; 12 - 13 10 den.default.includes = [ den._.mutual-provider ]; 14 11 15 12 den.aspects.igloo.provides.tux = den.lib.parametric { ··· 27 24 { 28 25 den.hosts.x86_64-linux.igloo.users.tux = { }; 29 26 30 - den.default.nixos.system.stateVersion = "25.11"; 31 - den.default.homeManager.home.stateVersion = "25.11"; 32 - 33 27 den.default.includes = [ den._.mutual-provider ]; 34 28 35 29 den.aspects.tux.provides.igloo = den.lib.parametric { ··· 46 40 { den, igloo, ... }: 47 41 { 48 42 den.hosts.x86_64-linux.igloo.users.tux = { }; 49 - 50 - den.default.nixos.system.stateVersion = "25.11"; 51 - den.default.homeManager.home.stateVersion = "25.11"; 52 43 53 44 den.default.includes = [ den._.mutual-provider ]; 54 45 ··· 57 48 }; 58 49 59 50 den.aspects.tux.provides.igloo = den.lib.parametric { 51 + nixos.boot.kernel.randstructSeed = "denseed"; 52 + }; 53 + 54 + expr = [ 55 + igloo.boot.kernel.randstructSeed 56 + igloo.home-manager.users.tux.home.keyboard.model 57 + ]; 58 + 59 + expected = [ 60 + "denseed" 61 + "denboard" 62 + ]; 63 + } 64 + ); 65 + 66 + test-for-all = denTest ( 67 + { den, igloo, ... }: 68 + { 69 + den.hosts.x86_64-linux.igloo.users.tux = { }; 70 + 71 + den.default.includes = [ den._.mutual-provider ]; 72 + 73 + den.aspects.igloo.provides.to-users = { 74 + homeManager.home.keyboard.model = "denboard"; 75 + }; 76 + 77 + den.aspects.tux.provides.to-hosts = { 60 78 nixos.boot.kernel.randstructSeed = "denseed"; 61 79 }; 62 80
+5 -1
templates/ci/modules/test-support/eval-den.nix
··· 32 32 options.flake.packages = lib.mkOption { }; 33 33 options.expr = lib.mkOption { }; 34 34 options.expected = lib.mkOption { }; 35 - config.den.schema.user.classes = lib.mkDefault [ "homeManager" ]; 35 + config = { 36 + den.schema.user.classes = lib.mkDefault [ "homeManager" ]; 37 + den.default.nixos.system.stateVersion = lib.mkDefault "25.11"; 38 + den.default.homeManager.home.stateVersion = lib.mkDefault "25.11"; 39 + }; 36 40 }; 37 41 38 42 helpersModule =