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(class): Allow test config values on forward classes. (#307)

authored by

Victor Borja and committed by
GitHub
35542c55 899fd3fb

+121 -24
+35 -8
docs/src/content/docs/guides/custom-classes.mdx
··· 107 107 108 108 ```nix 109 109 den.provides.forward { 110 - each = lib.singleton true; 111 - fromClass = _: "wsl"; 112 - intoClass = _: host.class; 113 - intoPath = _: [ "wsl" ]; 114 - fromAspect = _: lib.head aspect-chain; 110 + each = lib.singleton true; # <- true is the single item, mostly ignored. 111 + fromClass = _item: "wsl"; 112 + intoClass = _item: host.class; 113 + intoPath = _item: [ "wsl" ]; 114 + fromAspect = _item: lib.head aspect-chain; 115 + 115 116 # Only forward if target has wsl options 116 117 guard = { options, ... }: options ? wsl; 118 + 117 119 # Modify module args for the forwarded module 118 - adaptArgs = args: args // { osConfig = args.config; }; 120 + adaptArgs = args: { osConfig = args.config; }; 121 + 119 122 # Custom module type for the forwarded submodule 120 123 adapterModule = { config._module.freeformType = lib.types.anything; }; 121 124 } ··· 126 129 | `guard` | Only forward when this predicate returns true | 127 130 | `adaptArgs` | Transform module arguments before forwarding | 128 131 | `adapterModule` | Custom module for the forwarded submodule type | 132 + 133 + <Aside title="Guards reading config values"> 134 + 135 + From our previous example, both of these are exactly the same: 136 + 137 + ```nix "lib.optionalAttrs" 138 + guard = { options, ... }: options ? wsl; 139 + 140 + guard = { options, ... }: _item: lib.optionalAttrs (options ? wsl); 141 + ``` 142 + 143 + The second form is more advanced and can be used to read values from 144 + each item or from config values. 145 + 146 + For example, to test when vim is enabled, you have to use `mkIf`: 147 + 148 + ```nix "lib.mkIf" 149 + guard = { config, ... }: _item: lib.mkIf (config.programs.vim.enable); 150 + ``` 151 + 152 + - Use `lib.optionalAttrs` when testing if an **option is defined**. 153 + - Use `lib.mkIf` when testing for a **config value**. 154 + 155 + </Aside> 129 156 130 157 ## User contributed examples 131 158 ··· 188 215 }; 189 216 ``` 190 217 191 - #### Example: A git class that forwards to home-manager. 218 + #### Example: A git class that checks enable. 192 219 193 220 ```nix 194 221 gitClass = ··· 199 226 intoClass = _: "homeManager"; 200 227 intoPath = _: [ "programs" "git" ]; 201 228 fromAspect = _: lib.head aspect-chain; 202 - adaptArgs = lib.id; 229 + guard = { config, ... }: _: lib.mkIf config.programs.git.enable; 203 230 }; 204 231 205 232 den.aspects.tux = {
+27 -16
modules/aspects/provides/forward.nix
··· 26 26 Any other user-environments like `nix-maid` or `hjem` or user-custom classes 27 27 are easily implemented using `den._.forward`. 28 28 29 - Note: `den._.forward` is a high-level aspect, its result 30 - is another aspect that needs to be included for the new class to exist. 29 + Note: `den._.forward` returns an aspect that needs to be included for 30 + the new class to exist. 31 31 32 - See templates/ci/modules/forward.nix for usage example. 32 + See templates/ci/modules/guarded-forward.nix, templates/ci/modules/forward-from-custom-class.nix 33 33 See also: https://github.com/vic/den/issues/160, https://github.com/vic/flake-aspects/pull/31 34 34 ''; 35 35 ··· 50 50 "adaptArgs" 51 51 "adapterModule" 52 52 ]; 53 - fromClass = fwd.fromClass (lib.head fwd.each); 54 - intoClass = fwd.intoClass (lib.head fwd.each); 55 - intoPath = fwd.intoPath (lib.head fwd.each); 53 + item = lib.head fwd.each; 54 + fromClass = fwd.fromClass item; 55 + intoClass = fwd.intoClass item; 56 + intoPath = fwd.intoPath item; 56 57 freeformMod = { 57 58 config._module.freeformType = lib.types.lazyAttrsOf lib.types.unspecified; 58 59 }; 59 - adapterKey = lib.concatStringsSep "_" ( 60 + adapterKey = lib.concatStringsSep "/" ( 60 61 [ 61 62 fromClass 62 63 intoClass 63 64 ] 64 65 ++ intoPath 65 66 ); 67 + 68 + guardArgs = if guard == null then { } else lib.functionArgs guard; 69 + guardFn = 70 + args: guarded: 71 + let 72 + res = (if guard == null then _: true else guard) args; 73 + in 74 + if lib.isFunction res then res item guarded else lib.optionalAttrs res guarded; 75 + 66 76 adapter = { 67 77 includes = [ 68 78 (den.lib.aspects.forward ( ··· 76 86 } 77 87 )) 78 88 ]; 79 - ${intoClass} = args: { 80 - options.den.fwd.${adapterKey} = lib.mkOption { 81 - default = { }; 82 - type = lib.types.submoduleWith { 83 - specialArgs = if adaptArgs == null then args else adaptArgs args; 84 - modules = if adapterModule == null then [ freeformMod ] else [ adapterModule ]; 89 + ${intoClass} = { 90 + __functionArgs = guardArgs; 91 + __functor = _: args: { 92 + options.den.fwd.${adapterKey} = lib.mkOption { 93 + default = { }; 94 + type = lib.types.submoduleWith { 95 + specialArgs = if adaptArgs == null then args else adaptArgs args; 96 + modules = if adapterModule == null then [ freeformMod ] else [ adapterModule ]; 97 + }; 85 98 }; 99 + config = guardFn args (lib.setAttrByPath intoPath args.config.den.fwd.${adapterKey}); 86 100 }; 87 - config = lib.optionalAttrs (guard == null || guard args) ( 88 - lib.setAttrByPath intoPath args.config.den.fwd.${adapterKey} 89 - ); 90 101 }; 91 102 }; 92 103
+59
templates/ci/modules/features/guarded-forward.nix
··· 84 84 } 85 85 ); 86 86 87 + test-guard-can-read-config-values = denTest ( 88 + { 89 + den, 90 + lib, 91 + igloo, 92 + tuxHm, 93 + pinguHm, 94 + ... 95 + }: 96 + { 97 + 98 + den.hosts.x86_64-linux.igloo.users = { 99 + tux = { }; 100 + pingu = { }; 101 + }; 102 + 103 + den.schema.user.classes = [ "homeManager" ]; 104 + 105 + den.aspects.pingu.homeManager.programs.vim.enable = true; 106 + 107 + den.ctx.user.includes = 108 + let 109 + unset.homeManager.home.keyboard.model = lib.mkDefault "unset"; 110 + 111 + vimer-home = 112 + { class, aspect-chain }: 113 + den._.forward { 114 + each = lib.singleton true; 115 + fromAspect = _: lib.head aspect-chain; 116 + fromClass = _: "home-pingu"; 117 + intoClass = _: "homeManager"; 118 + intoPath = _: [ "home" ]; 119 + guard = { config, ... }: _: lib.mkIf config.programs.vim.enable; 120 + }; 121 + 122 + doit.home-pingu = 123 + { pkgs, ... }: 124 + { 125 + keyboard.model = lib.getName pkgs.hello; 126 + }; 127 + 128 + in 129 + [ 130 + unset 131 + doit 132 + vimer-home 133 + ]; 134 + 135 + expr = { 136 + tux = tuxHm.home.keyboard.model; 137 + pingu = pinguHm.home.keyboard.model; 138 + }; 139 + expected = { 140 + tux = "unset"; 141 + pingu = "hello"; 142 + }; 143 + } 144 + ); 145 + 87 146 }; 88 147 }