From d4f6801b87e22acc8c2e41c90c693c37dcdea370 Mon Sep 17 00:00:00 2001 From: "Dylan R. Johnston" Date: Wed, 1 Apr 2026 12:38:58 +0800 Subject: [PATCH 1/8] fix(namespace/providers): fix imported namespace provider reflection --- nix/lib/aspects/types.nix | 8 ++ nix/lib/namespace.nix | 5 +- templates/ci/flake.lock | 16 ++-- templates/ci/flake.nix | 3 +- ...rted-providers-function-arg-reflection.nix | 77 +++++++++++++++++++ 5 files changed, 97 insertions(+), 12 deletions(-) create mode 100644 templates/ci/modules/features/deadbugs/issue-xxx-exported-providers-function-arg-reflection.nix diff --git a/nix/lib/aspects/types.nix b/nix/lib/aspects/types.nix index abe973ba..8badc160 100644 --- a/nix/lib/aspects/types.nix +++ b/nix/lib/aspects/types.nix @@ -112,6 +112,14 @@ let type = functorType; default = cnf.defaultFunctor or lib.const; }; + + __functionArgs = lib.mkOption { + internal = true; + visible = false; + description = "Argument reflection information for the functor"; + type = lib.types.lazyAttrsOf lib.types.bool; + default = { }; + }; }; } ); diff --git a/nix/lib/namespace.nix b/nix/lib/namespace.nix index a55edc14..086ff397 100644 --- a/nix/lib/namespace.nix +++ b/nix/lib/namespace.nix @@ -36,7 +36,10 @@ let aspectPath: v: lib.optionals (builtins.isAttrs v) ( lib.optional (v ? __functor) { - config = lib.setAttrByPath aspectPath { __functor = v.__functor; }; + config = lib.setAttrByPath aspectPath { + __functor = v.__functor; + __functionArgs = v.__functionArgs or { }; + }; } ++ lib.concatMap ( pname: diff --git a/templates/ci/flake.lock b/templates/ci/flake.lock index e1a6146f..170487ec 100644 --- a/templates/ci/flake.lock +++ b/templates/ci/flake.lock @@ -22,18 +22,14 @@ }, "den": { "locked": { - "lastModified": 1774596030, - "narHash": "sha256-Gf24DeJaS0wttVoqYOiRPC3DrEKPeaQ5XYOXazzdLWA=", - "owner": "vic", - "repo": "den", - "rev": "91d41ed8b4a07e8b175ef2c9f8fc8470f0d75a41", - "type": "github" + "path": "../..", + "type": "path" }, "original": { - "owner": "vic", - "repo": "den", - "type": "github" - } + "path": "../..", + "type": "path" + }, + "parent": [] }, "home-manager": { "inputs": { diff --git a/templates/ci/flake.nix b/templates/ci/flake.nix index f479b3f4..1eb7c9e4 100644 --- a/templates/ci/flake.nix +++ b/templates/ci/flake.nix @@ -7,7 +7,8 @@ }).config.flake; inputs = { - den.url = "github:vic/den"; + # den.url = "github:vic/den"; + den.url = "path:../.."; import-tree.url = "github:vic/import-tree"; nixpkgs.url = "https://channels.nixos.org/nixpkgs-unstable/nixexprs.tar.xz"; diff --git a/templates/ci/modules/features/deadbugs/issue-xxx-exported-providers-function-arg-reflection.nix b/templates/ci/modules/features/deadbugs/issue-xxx-exported-providers-function-arg-reflection.nix new file mode 100644 index 00000000..355fc50e --- /dev/null +++ b/templates/ci/modules/features/deadbugs/issue-xxx-exported-providers-function-arg-reflection.nix @@ -0,0 +1,77 @@ +{ denTest, ... }: +{ + flake.tests.deadbugs-issue-xxx = + let + module = + { inputs, ... }: + { + imports = [ + inputs.den.flakeModule + (inputs.den.namespace "test" true) + ]; + + test.aspect._.host = + { host, ... }: + { + nixos.environment.sessionVariables.TEST_HOST = host.name; + }; + }; + internal = module; + external = + inputs: + (inputs.nixpkgs.lib.evalModules { + specialArgs = { inherit inputs; }; + modules = [ module ]; + }).config.flake; + in + { + test-internal-exported-providers-function-arg-reflection = denTest ( + { + lib, + test, + ... + }: + { + imports = [ internal ]; + + expr = lib.functionArgs test.aspect._.host; + expected = { + host = false; + }; + } + ); + + test-raw-exported-providers-function-arg-reflection = denTest ( + { + inputs, + lib, + test, + ... + }: + { + expr = lib.functionArgs (external inputs).denful.test.aspect._.host; + expected = { + host = false; + }; + } + ); + + test-external-exported-providers-function-arg-reflection = denTest ( + { + inputs, + test, + lib, + ... + }: + + { + imports = [ (inputs.den.namespace "test" [ (external inputs) ]) ]; + + expr = lib.functionArgs test.aspect._.host; + expected = { + host = false; + }; + } + ); + }; +} From d175f4c483c18920f30241890c7d598989cb3017 Mon Sep 17 00:00:00 2001 From: "Dylan R. Johnston" Date: Wed, 1 Apr 2026 12:43:04 +0800 Subject: [PATCH 2/8] update test bug number --- ...=> issue-352-exported-providers-function-arg-reflection.nix} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename templates/ci/modules/features/deadbugs/{issue-xxx-exported-providers-function-arg-reflection.nix => issue-352-exported-providers-function-arg-reflection.nix} (97%) diff --git a/templates/ci/modules/features/deadbugs/issue-xxx-exported-providers-function-arg-reflection.nix b/templates/ci/modules/features/deadbugs/issue-352-exported-providers-function-arg-reflection.nix similarity index 97% rename from templates/ci/modules/features/deadbugs/issue-xxx-exported-providers-function-arg-reflection.nix rename to templates/ci/modules/features/deadbugs/issue-352-exported-providers-function-arg-reflection.nix index 355fc50e..ab024add 100644 --- a/templates/ci/modules/features/deadbugs/issue-xxx-exported-providers-function-arg-reflection.nix +++ b/templates/ci/modules/features/deadbugs/issue-352-exported-providers-function-arg-reflection.nix @@ -1,6 +1,6 @@ { denTest, ... }: { - flake.tests.deadbugs-issue-xxx = + flake.tests.deadbugs-issue-352 = let module = { inputs, ... }: From 8fd64df07ac066b8b15e4f3f3135f99f2cf883b1 Mon Sep 17 00:00:00 2001 From: "Dylan R. Johnston" Date: Wed, 1 Apr 2026 13:04:54 +0800 Subject: [PATCH 3/8] fix optional attribute --- nix/lib/namespace.nix | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/nix/lib/namespace.nix b/nix/lib/namespace.nix index 086ff397..cd377b14 100644 --- a/nix/lib/namespace.nix +++ b/nix/lib/namespace.nix @@ -36,10 +36,14 @@ let aspectPath: v: lib.optionals (builtins.isAttrs v) ( lib.optional (v ? __functor) { - config = lib.setAttrByPath aspectPath { - __functor = v.__functor; - __functionArgs = v.__functionArgs or { }; - }; + config = lib.setAttrByPath aspectPath ( + { + __functor = v.__functor; + } + // lib.optionalAttrs (v ? __functionArgs) { + __functionArgs = v.__functionArgs; + } + ); } ++ lib.concatMap ( pname: From d9696ea08fca685864fa887aa0bbf5c1db272d00 Mon Sep 17 00:00:00 2001 From: "Dylan R. Johnston" Date: Wed, 1 Apr 2026 14:43:15 +0800 Subject: [PATCH 4/8] fix default function args --- nix/lib/aspects/types.nix | 39 ++++++++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/nix/lib/aspects/types.nix b/nix/lib/aspects/types.nix index 8badc160..c876ecca 100644 --- a/nix/lib/aspects/types.nix +++ b/nix/lib/aspects/types.nix @@ -20,6 +20,30 @@ let }; }; + lastDefType = lib.types.mkOptionType { + name = "last"; + description = "last value"; + merge = loc: defs: if defs == [ ] then { } else (lib.last defs).value; + }; + + partitionedType = + { + name, + description, + partition, + merge ? lastDefType.merge, + baseType, + }: + lib.types.mkOptionType { + inherit name description; + merge = + loc: defs: + let + partitioned = lib.lists.partition partition defs; + in + baseType.merge loc partitioned.wrong // (merge loc partitioned.right); + }; + isSubmoduleFn = m: let @@ -70,7 +94,12 @@ let lib.types.submodule ( { name, config, ... }: { - freeformType = lib.types.lazyAttrsOf lib.types.deferredModule; + freeformType = partitionedType { + name = "Aspect Freeform Type"; + description = "Aspect freeform type, has a hole in it for __functionArgs"; + partition = def: def.value ? __functionArgs; + baseType = lib.types.lazyAttrsOf lib.types.deferredModule; + }; config._module.args.aspect = config; imports = [ (lib.mkAliasOptionModule [ "_" ] [ "provides" ]) ]; @@ -112,14 +141,6 @@ let type = functorType; default = cnf.defaultFunctor or lib.const; }; - - __functionArgs = lib.mkOption { - internal = true; - visible = false; - description = "Argument reflection information for the functor"; - type = lib.types.lazyAttrsOf lib.types.bool; - default = { }; - }; }; } ); From 30f77cad8eb1b4335fcbf997927c77f9f92682ca Mon Sep 17 00:00:00 2001 From: "Dylan R. Johnston" Date: Wed, 1 Apr 2026 14:50:01 +0800 Subject: [PATCH 5/8] cleanup partitionedType definition --- nix/lib/aspects/types.nix | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/nix/lib/aspects/types.nix b/nix/lib/aspects/types.nix index c876ecca..af06e793 100644 --- a/nix/lib/aspects/types.nix +++ b/nix/lib/aspects/types.nix @@ -31,7 +31,7 @@ let name, description, partition, - merge ? lastDefType.merge, + partitionType, baseType, }: lib.types.mkOptionType { @@ -41,7 +41,7 @@ let let partitioned = lib.lists.partition partition defs; in - baseType.merge loc partitioned.wrong // (merge loc partitioned.right); + baseType.merge loc partitioned.wrong // (partitionType.merge loc partitioned.right); }; isSubmoduleFn = @@ -98,6 +98,7 @@ let name = "Aspect Freeform Type"; description = "Aspect freeform type, has a hole in it for __functionArgs"; partition = def: def.value ? __functionArgs; + partitionType = lastDefType; baseType = lib.types.lazyAttrsOf lib.types.deferredModule; }; config._module.args.aspect = config; From cff616379b071b6fb1cf0c0324d8e9604b43d9bd Mon Sep 17 00:00:00 2001 From: Victor Borja Date: Thu, 2 Apr 2026 13:37:00 -0600 Subject: [PATCH 6/8] restore files from main --- nix/lib/aspects/types.nix | 32 +------------------------------- nix/lib/namespace.nix | 9 +-------- 2 files changed, 2 insertions(+), 39 deletions(-) diff --git a/nix/lib/aspects/types.nix b/nix/lib/aspects/types.nix index af06e793..abe973ba 100644 --- a/nix/lib/aspects/types.nix +++ b/nix/lib/aspects/types.nix @@ -20,30 +20,6 @@ let }; }; - lastDefType = lib.types.mkOptionType { - name = "last"; - description = "last value"; - merge = loc: defs: if defs == [ ] then { } else (lib.last defs).value; - }; - - partitionedType = - { - name, - description, - partition, - partitionType, - baseType, - }: - lib.types.mkOptionType { - inherit name description; - merge = - loc: defs: - let - partitioned = lib.lists.partition partition defs; - in - baseType.merge loc partitioned.wrong // (partitionType.merge loc partitioned.right); - }; - isSubmoduleFn = m: let @@ -94,13 +70,7 @@ let lib.types.submodule ( { name, config, ... }: { - freeformType = partitionedType { - name = "Aspect Freeform Type"; - description = "Aspect freeform type, has a hole in it for __functionArgs"; - partition = def: def.value ? __functionArgs; - partitionType = lastDefType; - baseType = lib.types.lazyAttrsOf lib.types.deferredModule; - }; + freeformType = lib.types.lazyAttrsOf lib.types.deferredModule; config._module.args.aspect = config; imports = [ (lib.mkAliasOptionModule [ "_" ] [ "provides" ]) ]; diff --git a/nix/lib/namespace.nix b/nix/lib/namespace.nix index cd377b14..a55edc14 100644 --- a/nix/lib/namespace.nix +++ b/nix/lib/namespace.nix @@ -36,14 +36,7 @@ let aspectPath: v: lib.optionals (builtins.isAttrs v) ( lib.optional (v ? __functor) { - config = lib.setAttrByPath aspectPath ( - { - __functor = v.__functor; - } - // lib.optionalAttrs (v ? __functionArgs) { - __functionArgs = v.__functionArgs; - } - ); + config = lib.setAttrByPath aspectPath { __functor = v.__functor; }; } ++ lib.concatMap ( pname: From 2b4680fc2283cb6f58ce86cb6ae297c82bb9d439 Mon Sep 17 00:00:00 2001 From: Victor Borja Date: Thu, 2 Apr 2026 09:28:15 -0600 Subject: [PATCH 7/8] Fix bug --- nix/lib/aspects/default.nix | 5 ++-- nix/lib/aspects/resolve.nix | 2 +- nix/lib/aspects/types.nix | 28 ++++-------------- nix/lib/default.nix | 1 + nix/lib/last-function-to.nix | 8 ++++++ nix/lib/namespace.nix | 55 ++---------------------------------- nix/lib/parametric.nix | 9 +++++- 7 files changed, 29 insertions(+), 79 deletions(-) create mode 100644 nix/lib/last-function-to.nix diff --git a/nix/lib/aspects/default.nix b/nix/lib/aspects/default.nix index 40ceb553..0379d65f 100644 --- a/nix/lib/aspects/default.nix +++ b/nix/lib/aspects/default.nix @@ -4,11 +4,12 @@ ... }: let + rawTypes = import ./types.nix { inherit den lib; }; + resolve = import ./resolve.nix { inherit den lib; }; + defaultFunctor = (den.lib.parametric { }).__functor; typesConf = { inherit defaultFunctor; }; - rawTypes = import ./types.nix lib; types = lib.mapAttrs (_: v: v typesConf) rawTypes; - resolve = import ./resolve.nix lib; in { inherit types resolve; diff --git a/nix/lib/aspects/resolve.nix b/nix/lib/aspects/resolve.nix index 3cabaace..537b129c 100644 --- a/nix/lib/aspects/resolve.nix +++ b/nix/lib/aspects/resolve.nix @@ -1,4 +1,4 @@ -lib: +{ lib, ... }: let resolve = diff --git a/nix/lib/aspects/types.nix b/nix/lib/aspects/types.nix index abe973ba..8edf0716 100644 --- a/nix/lib/aspects/types.nix +++ b/nix/lib/aspects/types.nix @@ -1,24 +1,6 @@ -lib: +{ lib, den, ... }: let - functorType = lib.types.mkOptionType { - name = "aspectFunctor"; - description = "aspect functor function"; - check = lib.isFunction; - merge = - _loc: defs: - let - lastDef = lib.last defs; - in - { - __functionArgs = lib.functionArgs lastDef.value; - __functor = - _: callerArgs: - let - result = lastDef.value callerArgs; - in - if builtins.isFunction result then result else _: result; - }; - }; + inherit (den.lib) lastFunctionTo; isSubmoduleFn = m: @@ -45,11 +27,11 @@ let names != [ ] && builtins.all (n: builtins.elem n providerArgNames) names; directProviderFn = - cnf: lib.types.addCheck (lib.types.functionTo (aspectSubmodule cnf)) isProviderFn; + cnf: lib.types.addCheck (lastFunctionTo (aspectSubmodule cnf)) isProviderFn; curriedProviderFn = cnf: - lib.types.addCheck (lib.types.functionTo (providerType cnf)) ( + lib.types.addCheck (lastFunctionTo (providerType cnf)) ( f: builtins.isFunction f || @@ -109,7 +91,7 @@ let internal = true; visible = false; description = "Functor to default provider"; - type = functorType; + type = lastFunctionTo (providerType cnf); default = cnf.defaultFunctor or lib.const; }; }; diff --git a/nix/lib/default.nix b/nix/lib/default.nix index 101373c2..bca1ee64 100644 --- a/nix/lib/default.nix +++ b/nix/lib/default.nix @@ -31,6 +31,7 @@ let parametric = ./parametric.nix; statics = ./statics.nix; take = ./take.nix; + lastFunctionTo = ./last-function-to.nix; }; in den-lib diff --git a/nix/lib/last-function-to.nix b/nix/lib/last-function-to.nix new file mode 100644 index 00000000..4507836c --- /dev/null +++ b/nix/lib/last-function-to.nix @@ -0,0 +1,8 @@ +{ lib, ... }: +elemType: +lib.types.mkOptionType { + name = "lastFunctionTo"; + description = "last function to ${elemType.description}"; + check = (lib.types.functionTo elemType).check; + merge = _loc: defs: (lib.last defs).value; +} diff --git a/nix/lib/namespace.nix b/nix/lib/namespace.nix index a55edc14..d2e29e9a 100644 --- a/nix/lib/namespace.nix +++ b/nix/lib/namespace.nix @@ -8,54 +8,7 @@ let name ]) (builtins.filter builtins.isAttrs from); - internals = [ - "_" - "_module" - "__functor" - "__functionArgs" - ]; - - stripAspect = - v: - if !builtins.isAttrs v then - v - else - lib.mapAttrs ( - n: child: - if n == "provides" then - lib.mapAttrs (_: stripAspect) child - else if builtins.isAttrs child then - builtins.removeAttrs child internals - else - child - ) (builtins.removeAttrs v internals); - - stripNamespace = lib.mapAttrs (_: stripAspect); - - functorModules = - aspectPath: v: - lib.optionals (builtins.isAttrs v) ( - lib.optional (v ? __functor) { - config = lib.setAttrByPath aspectPath { __functor = v.__functor; }; - } - ++ lib.concatMap ( - pname: - functorModules ( - aspectPath - ++ [ - "provides" - pname - ] - ) v.provides.${pname} - ) (lib.attrNames (v.provides or { })) - ); - - namespaceFunctorModules = - ns: lib.concatMap (aname: functorModules [ "den" "ful" name aname ] ns.${aname}) (lib.attrNames ns); - - sourceModule = { - config.den.ful.${name} = lib.mkMerge (map stripNamespace denfuls); - }; + sourceModules = map (denful: { config.den.ful.${name} = denful; }) denfuls; aliasModule = lib.mkAliasOptionModule [ name ] [ "den" "ful" name ]; @@ -68,11 +21,9 @@ let { }; in { - imports = [ - sourceModule + imports = sourceModules ++ [ aliasModule outputModule - ] - ++ lib.concatMap namespaceFunctorModules denfuls; + ]; config._module.args.${name} = config.den.ful.${name}; } diff --git a/nix/lib/parametric.nix b/nix/lib/parametric.nix index cdbdc986..9e08eb62 100644 --- a/nix/lib/parametric.nix +++ b/nix/lib/parametric.nix @@ -17,7 +17,14 @@ let provided // { includes = map ( - include: if include ? includes then parametric.deep functor ctx include else include + include: + if include ? includes then + if include ? __functor then + include + else + parametric.deep functor ctx include + else + include ) (provided.includes or [ ]); }; From b9c0211e8bbcb57c3a2c3dab5d54eac2007e10e5 Mon Sep 17 00:00:00 2001 From: Victor Borja Date: Thu, 2 Apr 2026 13:40:35 -0600 Subject: [PATCH 8/8] fmt --- README.md | 2 -- nix/lib/aspects/types.nix | 3 +-- nix/lib/parametric.nix | 5 +---- 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index ac508aca..726701cd 100644 --- a/README.md +++ b/README.md @@ -16,8 +16,6 @@ These configurations become specific when applied to your particular infra entities (hosts/users), while allowing re-usable aspects to be shared between hosts, users, or across other flakes and non-flake projects. - -
diff --git a/nix/lib/aspects/types.nix b/nix/lib/aspects/types.nix index 8edf0716..4978cf23 100644 --- a/nix/lib/aspects/types.nix +++ b/nix/lib/aspects/types.nix @@ -26,8 +26,7 @@ let in names != [ ] && builtins.all (n: builtins.elem n providerArgNames) names; - directProviderFn = - cnf: lib.types.addCheck (lastFunctionTo (aspectSubmodule cnf)) isProviderFn; + directProviderFn = cnf: lib.types.addCheck (lastFunctionTo (aspectSubmodule cnf)) isProviderFn; curriedProviderFn = cnf: diff --git a/nix/lib/parametric.nix b/nix/lib/parametric.nix index 9e08eb62..e8619cd8 100644 --- a/nix/lib/parametric.nix +++ b/nix/lib/parametric.nix @@ -19,10 +19,7 @@ let includes = map ( include: if include ? includes then - if include ? __functor then - include - else - parametric.deep functor ctx include + if include ? __functor then include else parametric.deep functor ctx include else include ) (provided.includes or [ ]);