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: structural provider provenance via meta.provider (#399)

**Summary**
- Adds meta.provider typed option tracking each aspect's structural
origin as a path (e.g., ["foo" "bar"] for foo.provides.bar)
- providerPrefix threads through the provides submodule so child
providers accumulate their parent's path
- mkAspectsType factory allows namespaces and den.provides to set their
own provider root

### How it works

The provides submodule extends providerPrefix with the current aspect's
name before passing it to child provider types. Namespaces set
providerPrefix = [ name ] as their root. den.provides uses [ "den"
"provides" ]. Top-level aspects default to [].

This lets adapters distinguish "bar provided by foo" from a top-level
"bar" by inspecting aspect.meta.provider.

authored by

Jason Bowman and committed by
GitHub
6ca251e7 f09f211b

+103 -7
+8 -1
modules/aspects/provides.nix
··· 10 10 default = { }; 11 11 description = "Batteries Included - re-usable high-level aspects"; 12 12 type = lib.types.submodule { 13 - freeformType = lib.types.attrsOf config.den.lib.aspects.types.providerType; 13 + freeformType = lib.types.attrsOf ( 14 + (config.den.lib.aspects.mkAspectsType { 15 + providerPrefix = [ 16 + "den" 17 + "provides" 18 + ]; 19 + }).providerType 20 + ); 14 21 }; 15 22 }; 16 23 };
+1
nix/lib/aspects/default.nix
··· 14 14 in 15 15 { 16 16 inherit types adapters resolve; 17 + mkAspectsType = cnf': lib.mapAttrs (_: v: v (typesConf // cnf')) rawTypes; 17 18 }
+15 -1
nix/lib/aspects/types.nix
··· 107 107 type = lib.types.nullOr (lastFunctionTo lib.types.raw); 108 108 default = null; 109 109 }; 110 + options.provider = lib.mkOption { 111 + internal = true; 112 + visible = false; 113 + description = "Provider path tracking aspect provenance"; 114 + type = lib.types.listOf lib.types.str; 115 + default = cnf.providerPrefix or [ ]; 116 + }; 110 117 }; 111 118 defaultText = lib.literalExpression "{ }"; 112 119 default = { }; ··· 124 131 defaultText = lib.literalExpression "{ }"; 125 132 default = { }; 126 133 type = lib.types.submodule { 127 - freeformType = lib.types.lazyAttrsOf (providerType cnf); 134 + freeformType = lib.types.lazyAttrsOf ( 135 + providerType ( 136 + cnf 137 + // { 138 + providerPrefix = (cnf.providerPrefix or [ ]) ++ [ config.name ]; 139 + } 140 + ) 141 + ); 128 142 }; 129 143 }; 130 144
+3 -3
nix/lib/namespace-types.nix
··· 1 1 { den, lib, ... }: 2 2 let 3 3 inherit (den.lib) ctxApply; 4 - inherit (den.lib.aspects.types) aspectsType; 4 + inherit (den.lib.aspects) mkAspectsType; 5 5 6 6 namespaceType = lib.types.submodule ( 7 - nsArgs: 7 + nsArgs@{ name, ... }: 8 8 let 9 9 nsCtxApply = ctxApply nsArgs.config.ctx; 10 10 inherit (den.lib.ctxTypes nsCtxApply) ctxTreeType; ··· 24 24 freeformType = lib.types.lazyAttrsOf lib.types.deferredModule; 25 25 }; 26 26 }; 27 - freeformType = aspectsType; 27 + freeformType = (mkAspectsType { providerPrefix = [ name ]; }).aspectsType; 28 28 } 29 29 ); 30 30 in
+2 -2
templates/ci/modules/features/aspect-meta.nix
··· 80 80 KEYS 81 81 ; 82 82 }; 83 - expected.KEYS = "adapter:file:loc:name:self"; 83 + expected.KEYS = "adapter:file:loc:name:provider:self"; 84 84 } 85 85 ); 86 86 ··· 110 110 KEYS 111 111 ; 112 112 }; 113 - expected.KEYS = "adapter:file:foo:loc:name:self"; 113 + expected.KEYS = "adapter:file:foo:loc:name:provider:self"; 114 114 } 115 115 ); 116 116
+74
templates/ci/modules/features/provider-provenance.nix
··· 1 + { denTest, lib, ... }: 2 + { 3 + flake.tests.provider-provenance = 4 + let 5 + getProvenance = 6 + { aspect, recurse, ... }: 7 + { 8 + name = aspect.name; 9 + provider = aspect.meta.provider or [ ]; 10 + children = map (i: recurse i) (aspect.includes or [ ]); 11 + }; 12 + in 13 + { 14 + 15 + test-top-level-has-empty-provider = denTest ( 16 + { den, ... }: 17 + { 18 + den.aspects.foo.nixos = { }; 19 + 20 + expr = (den.lib.aspects.resolve.withAdapter getProvenance "nixos" den.aspects.foo).provider; 21 + expected = [ ]; 22 + } 23 + ); 24 + 25 + test-provided-aspect-has-provider-path = denTest ( 26 + { den, ... }: 27 + { 28 + den.aspects.foo.includes = [ den.aspects.foo._.bar ]; 29 + den.aspects.foo._.bar.nixos = { }; 30 + 31 + expr = 32 + let 33 + result = den.lib.aspects.resolve.withAdapter getProvenance "nixos" den.aspects.foo; 34 + in 35 + (lib.head result.children).provider; 36 + expected = [ "foo" ]; 37 + } 38 + ); 39 + 40 + test-deep-provider-chain = denTest ( 41 + { den, ... }: 42 + { 43 + den.aspects.foo._.bar.includes = [ den.aspects.foo._.bar._.baz ]; 44 + den.aspects.foo._.bar._.baz.nixos = { }; 45 + den.aspects.foo.includes = [ den.aspects.foo._.bar ]; 46 + 47 + expr = 48 + let 49 + result = den.lib.aspects.resolve.withAdapter getProvenance "nixos" den.aspects.foo; 50 + barResult = lib.head result.children; 51 + in 52 + (lib.head barResult.children).provider; 53 + expected = [ 54 + "foo" 55 + "bar" 56 + ]; 57 + } 58 + ); 59 + 60 + test-namespace-provider-root = denTest ( 61 + { den, ... }: 62 + { 63 + den.provides.myaspect.nixos = { }; 64 + 65 + expr = (den.lib.aspects.resolve.withAdapter getProvenance "nixos" den.provides.myaspect).provider; 66 + expected = [ 67 + "den" 68 + "provides" 69 + ]; 70 + } 71 + ); 72 + 73 + }; 74 + }