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: don't coerce factory-function aspects (#437)

PR #410 added coercedProviderType to make `{host, ...}: {nixos = ...}`
aspects merge with sibling static `.nixos = ...` defs (fixes #408). The
predicate was too broad: any non-module function got wrapped into `{
includes = [fn] }`, which silently broke "factory" aspects like:

den.aspects.facter = reportPath: { nixos = ...; };
den.aspects.igloo.includes = [ (den.aspects.facter "/path") ];

Coercing turns den.aspects.facter into a full aspect with the default
functor. Calling `(facter "/path")` invokes the default functor in a
non-static branch with the string as `ctx` — the user's `reportPath` is
discarded and the config body never materializes.

Narrow the predicate with `lib.functionArgs v != {}`. Context fns have
destructured named args (non-empty functionArgs) and still get coerced.
Factory fns with a bare positional arg stay typed as providerFnType,
whose merge wraps the underlying function via
`__functor = _: eth.merge loc defs` so `(aspect arg)` correctly
dispatches to the user function.

Bisected to d266c3a (PR #410). Minimal repro verified broken at that
commit and working at v0.15.0. Fix passes the new deadbugs test plus all
278 existing CI tests including deadbugs-issue-408 (`context-fn +
static` merge still works).

Fixes #429.

authored by

Jason Bowman and committed by
GitHub
612ce704 ec780bb7

+41 -1
+12 -1
nix/lib/aspects/types.nix
··· 156 156 157 157 # Wrap non-module functions into { includes = [fn] } so they don't get 158 158 # treated as module functions by aspectType's submodule merge. 159 + # 160 + # Only coerce functions that destructure a context attrset 161 + # (`{ host, ... }: ...`, `{ user, ... }: ...`, etc.) — those have a 162 + # non-empty `functionArgs`. Bare-arg "factory" functions like 163 + # `facter = reportPath: { nixos = ...; }` have empty `functionArgs` 164 + # and must NOT be coerced — coercing them turns `den.aspects.facter` 165 + # into a full aspect whose default functor ignores the user's 166 + # argument, so `(facter ./facter.json)` no longer materializes the 167 + # config. Such functions stay typed as `providerFnType`, whose merge 168 + # wraps the underlying function via `__functor = _: eth.merge loc 169 + # defs` so `(aspect arg)` correctly invokes the user function. 159 170 coercedProviderType = 160 171 cnf: 161 172 let 162 173 pt = providerType cnf; 163 174 in 164 175 lib.types.coercedTo (lib.types.addCheck lib.types.raw ( 165 - v: builtins.isFunction v && !isSubmoduleFn v 176 + v: builtins.isFunction v && !isSubmoduleFn v && lib.functionArgs v != { } 166 177 )) (fn: { includes = [ fn ]; }) pt; 167 178 168 179 aspectsType =
+29
templates/ci/modules/features/deadbugs/issue-429-parametrized-aspect-call-at-include.nix
··· 1 + # Minimal reproduction of drupol's pattern from issue #429 2 + # A factory-function aspect called at include time, returning nixos config. 3 + { denTest, ... }: 4 + { 5 + flake.tests.deadbugs-issue-429 = { 6 + 7 + test-parametrized-aspect-call-at-include = denTest ( 8 + { den, igloo, ... }: 9 + { 10 + den.hosts.x86_64-linux.igloo.users.tux = { }; 11 + 12 + imports = [ 13 + { 14 + den.aspects.facter = reportPath: { 15 + nixos.environment.variables.FACTER_REPORT = reportPath; 16 + }; 17 + } 18 + { 19 + den.aspects.igloo.includes = [ (den.aspects.facter "/path/to/report") ]; 20 + } 21 + ]; 22 + 23 + expr = igloo.environment.variables.FACTER_REPORT or null; 24 + expected = "/path/to/report"; 25 + } 26 + ); 27 + 28 + }; 29 + }