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.

fix: propagate context through named parametric includes (#444)

Named aspects coerced from parametric fns (e.g. `den.aspects.git = {
user, ... }: { nixos = ...; }`) had their context silently dropped when
included from other parametric aspects. applyDeep's sub-recursion now
reaches into named aspects' includes to apply context to the parametric
functions inside.

Fixes #442.

authored by

Jason Bowman and committed by
GitHub
dca63688 5870c478

+225 -1
+15 -1
nix/lib/parametric.nix
··· 59 59 # class configs must be picked up later by the static resolve pass. 60 60 # A user-provided provider fn (e.g. { host, ... }: { nixos = ...; }) 61 61 # has host in functionArgs; canTake.upTo fires and we materialize it. 62 + # 63 + # Named aspects (coerced from parametric fns by coercedProviderType) 64 + # also have functionArgs = {} on their default functor, so 65 + # canTake.upTo fails for them too. But their includes may contain 66 + # unapplied parametric functions that need context. For these, recurse 67 + # into the aspect's includes directly — applying context to each fn 68 + # include without invoking the aspect's own functor (which would drop 69 + # owned class configs on static aspects per #423). 62 70 includes = map ( 63 - sub: if canTake.upTo ctx sub then carryMeta sub (take.upTo sub ctx) else sub 71 + sub: 72 + if canTake.upTo ctx sub then 73 + carryMeta sub (take.upTo sub ctx) 74 + else if builtins.isAttrs sub && sub ? __functor && sub ? includes then 75 + sub // { includes = map (applyDeep takeFn ctx) (sub.includes or [ ]); } 76 + else 77 + sub 64 78 ) rRaw.includes; 65 79 } 66 80 else
+101
templates/ci/modules/features/deadbugs/issue-442-parametric-included-by-parametric.nix
··· 1 + # Parametric aspects included by parametric aspects are not applied. 2 + # When a named parametric aspect (context-destructuring fn) includes another 3 + # named parametric aspect, the coercedProviderType wraps both into 4 + # { includes = [fn]; __functor = defaultFunctor; }. mapIncludes was skipping 5 + # includes that have __functor, so the inner parametric fn never received 6 + # user/host context and its config was silently dropped. 7 + # https://github.com/vic/den/issues/442 8 + { denTest, ... }: 9 + { 10 + flake.tests.deadbugs-issue-442 = { 11 + 12 + # Exact reproducer from the issue: parametric dev includes parametric git. 13 + test-parametric-aspect-included-by-parametric-aspect = denTest ( 14 + { den, igloo, ... }: 15 + { 16 + den.hosts.x86_64-linux.igloo.users.tux = { }; 17 + 18 + den.aspects.git = 19 + { user, ... }: 20 + { 21 + nixos = 22 + { ... }: 23 + { 24 + programs.git.enable = true; 25 + }; 26 + }; 27 + 28 + den.aspects.dev = 29 + { user, ... }: 30 + { 31 + includes = [ 32 + den.aspects.git 33 + ]; 34 + }; 35 + 36 + den.aspects.tux = { 37 + includes = [ 38 + den.aspects.dev 39 + ]; 40 + }; 41 + 42 + expr = igloo.programs.git.enable; 43 + expected = true; 44 + } 45 + ); 46 + 47 + # Three-deep chain: static -> parametric -> parametric -> parametric. 48 + test-triple-nested-parametric-includes = denTest ( 49 + { 50 + den, 51 + igloo, 52 + lib, 53 + ... 54 + }: 55 + { 56 + den.hosts.x86_64-linux.igloo.users.tux = { }; 57 + 58 + den.aspects.shell = 59 + { user, ... }: 60 + { 61 + nixos = 62 + { ... }: 63 + { 64 + programs.zsh.enable = true; 65 + }; 66 + }; 67 + 68 + den.aspects.dev = 69 + { user, ... }: 70 + { 71 + includes = [ den.aspects.shell ]; 72 + nixos = 73 + { ... }: 74 + { 75 + programs.git.enable = true; 76 + }; 77 + }; 78 + 79 + den.aspects.role = 80 + { user, ... }: 81 + { 82 + includes = [ den.aspects.dev ]; 83 + }; 84 + 85 + den.aspects.tux = { 86 + includes = [ den.aspects.role ]; 87 + }; 88 + 89 + expr = { 90 + git = igloo.programs.git.enable; 91 + zsh = igloo.programs.zsh.enable; 92 + }; 93 + expected = { 94 + git = true; 95 + zsh = true; 96 + }; 97 + } 98 + ); 99 + 100 + }; 101 + }
+109
templates/ci/modules/features/parametric.nix
··· 90 90 } 91 91 ); 92 92 93 + # Parametric aspect including a static named aspect — owned configs 94 + # on the static aspect must not be dropped by applyDeep recursion. 95 + test-parametric-including-static-named-aspect = denTest ( 96 + { den, igloo, ... }: 97 + { 98 + den.hosts.x86_64-linux.igloo.users.tux = { }; 99 + 100 + den.aspects.base.nixos = 101 + { ... }: 102 + { 103 + programs.git.enable = true; 104 + }; 105 + 106 + den.aspects.dev = 107 + { user, ... }: 108 + { 109 + includes = [ den.aspects.base ]; 110 + }; 111 + 112 + den.aspects.tux.includes = [ den.aspects.dev ]; 113 + 114 + expr = igloo.programs.git.enable; 115 + expected = true; 116 + } 117 + ); 118 + 119 + # Named aspect with both owned class config and parametric includes, 120 + # referenced from inside a parametric function's bare result. 121 + test-parametric-including-mixed-owned-and-parametric = denTest ( 122 + { 123 + den, 124 + igloo, 125 + lib, 126 + ... 127 + }: 128 + { 129 + den.hosts.x86_64-linux.igloo.users.tux = { }; 130 + 131 + den.aspects.tools = { 132 + nixos = 133 + { ... }: 134 + { 135 + programs.git.enable = true; 136 + }; 137 + includes = [ 138 + ( 139 + { user, ... }: 140 + { 141 + nixos = 142 + { ... }: 143 + { 144 + programs.zsh.enable = true; 145 + }; 146 + } 147 + ) 148 + ]; 149 + }; 150 + 151 + den.aspects.role = 152 + { user, ... }: 153 + { 154 + includes = [ den.aspects.tools ]; 155 + }; 156 + 157 + den.aspects.tux.includes = [ den.aspects.role ]; 158 + 159 + expr = { 160 + git = igloo.programs.git.enable; 161 + zsh = igloo.programs.zsh.enable; 162 + }; 163 + expected = { 164 + git = true; 165 + zsh = true; 166 + }; 167 + } 168 + ); 169 + 170 + # Factory function aspect called inside a nested parametric chain. 171 + test-factory-in-nested-parametric-chain = denTest ( 172 + { 173 + den, 174 + igloo, 175 + lib, 176 + ... 177 + }: 178 + { 179 + den.hosts.x86_64-linux.igloo.users.tux = { }; 180 + 181 + den.aspects.greeter = greeting: { 182 + nixos = 183 + { ... }: 184 + { 185 + users.users.tux.description = greeting; 186 + }; 187 + }; 188 + 189 + den.aspects.role = 190 + { user, ... }: 191 + { 192 + includes = [ (den.aspects.greeter "hello") ]; 193 + }; 194 + 195 + den.aspects.tux.includes = [ den.aspects.role ]; 196 + 197 + expr = igloo.users.users.tux.description; 198 + expected = "hello"; 199 + } 200 + ); 201 + 93 202 test-never-matches-aspect-skipped = denTest ( 94 203 { den, igloo, ... }: 95 204 let