forked from oeiuwq.com/den
Modular, context-aware and aspect-oriented dendritic Nix configurations. Discussions: https://oeiuwq.zulipchat.com/join/nqp26cd4kngon6mo3ncgnuap/

Compare changes

Choose any two refs to compare.

+237 -12
+1 -1
.github/workflows/test.yml
··· 44 - run: nix flake update den 45 - run: nix run .#write-flake 46 - run: nix flake metadata 47 - - run: nix flake check
··· 44 - run: nix flake update den 45 - run: nix run .#write-flake 46 - run: nix flake metadata 47 + - run: nix flake check -L
+50 -6
nix/namespace.nix
··· 3 let 4 from = lib.flatten [ sources ]; 5 isOutput = builtins.any (x: x == true) from; 6 - denfuls = map (lib.getAttrFromPath [ 7 - "denful" 8 - name 9 - ]) (builtins.filter builtins.isAttrs from); 10 11 sourceModule = { 12 - config.den.ful.${name} = lib.mkMerge denfuls; 13 }; 14 15 aliasModule = lib.mkAliasOptionModule [ name ] [ "den" "ful" name ]; ··· 17 outputModule = 18 if isOutput then 19 { 20 - config.flake.denful.${name} = config.den.ful.${name}; 21 } 22 else 23 { };
··· 3 let 4 from = lib.flatten [ sources ]; 5 isOutput = builtins.any (x: x == true) from; 6 + attrs = builtins.filter builtins.isAttrs from; 7 + 8 + # Strip module system metadata to get clean raw values 9 + stripMeta = value: 10 + if builtins.isList value then 11 + map stripMeta value 12 + else if builtins.isAttrs value then 13 + let 14 + # Remove module system special attributes 15 + cleaned = builtins.removeAttrs value [ 16 + "__functor" 17 + "__functionArgs" 18 + "_module" 19 + "config" 20 + ]; 21 + in 22 + lib.mapAttrs (_: stripMeta) cleaned 23 + else 24 + value; 25 + 26 + # Deep merge that concatenates lists instead of overwriting them 27 + deepMergeWith = lhs: rhs: 28 + if builtins.isList lhs && builtins.isList rhs then 29 + lhs ++ rhs 30 + else if builtins.isAttrs lhs && builtins.isAttrs rhs then 31 + let 32 + allKeys = lib.unique (builtins.attrNames lhs ++ builtins.attrNames rhs); 33 + mergedAttrs = builtins.listToAttrs (map (name: { 34 + inherit name; 35 + value = 36 + if lhs ? ${name} && rhs ? ${name} then 37 + deepMergeWith lhs.${name} rhs.${name} 38 + else if lhs ? ${name} then 39 + lhs.${name} 40 + else 41 + rhs.${name}; 42 + }) allKeys); 43 + in 44 + mergedAttrs 45 + else 46 + rhs; 47 + 48 + # Extract denful values, strip metadata, and merge them deeply before passing to module system 49 + deepMerge = builtins.foldl' (acc: x: 50 + deepMergeWith acc (stripMeta (lib.getAttrFromPath [ "denful" name ] x)) 51 + ) { } attrs; 52 53 sourceModule = { 54 + config.den.ful.${name} = deepMerge; 55 }; 56 57 aliasModule = lib.mkAliasOptionModule [ name ] [ "den" "ful" name ]; ··· 59 outputModule = 60 if isOutput then 61 { 62 + # Use mkOptionDefault to ensure this assignment has lower priority 63 + # This prevents re-evaluation and duplication issues 64 + config.flake.denful.${name} = lib.mkOptionDefault config.den.ful.${name}; 65 } 66 else 67 { };
+33 -4
templates/examples/flake.lock
··· 22 }, 23 "den": { 24 "locked": { 25 - "lastModified": 1763707606, 26 - "narHash": "sha256-l9v3NNdKj3GJvV5LhzsWDs4Sl2bg0tuKNFFkMeFvUWo=", 27 "owner": "vic", 28 "repo": "den", 29 - "rev": "8164e0d89c59839d67757bc9a1fb61770dc6e8b7", 30 "type": "github" 31 }, 32 "original": { ··· 251 "nixpkgs" 252 ], 253 "nixpkgs-stable": "nixpkgs-stable", 254 - "systems": "systems" 255 } 256 }, 257 "systems": { ··· 268 "repo": "default", 269 "type": "github" 270 } 271 } 272 }, 273 "root": "root",
··· 22 }, 23 "den": { 24 "locked": { 25 + "lastModified": 1766081768, 26 + "narHash": "sha256-8Ea1DW3YZHifezfdEFHWEIpZBNKvEL+3iFOEcl3eFBU=", 27 "owner": "vic", 28 "repo": "den", 29 + "rev": "7271da18c60ab4d7c275ecaab480d29729f05d17", 30 "type": "github" 31 }, 32 "original": { ··· 251 "nixpkgs" 252 ], 253 "nixpkgs-stable": "nixpkgs-stable", 254 + "systems": "systems", 255 + "theirs": "theirs" 256 } 257 }, 258 "systems": { ··· 269 "repo": "default", 270 "type": "github" 271 } 272 + }, 273 + "theirs": { 274 + "inputs": { 275 + "den": [ 276 + "den" 277 + ], 278 + "flake-aspects": [ 279 + "flake-aspects" 280 + ], 281 + "flake-parts": [ 282 + "flake-parts" 283 + ], 284 + "import-tree": [ 285 + "import-tree" 286 + ], 287 + "nixpkgs": [ 288 + "nixpkgs" 289 + ] 290 + }, 291 + "locked": { 292 + "path": "./modules/_example/ci/_theirs", 293 + "type": "path" 294 + }, 295 + "original": { 296 + "path": "./modules/_example/ci/_theirs", 297 + "type": "path" 298 + }, 299 + "parent": [] 300 } 301 }, 302 "root": "root",
+10
templates/examples/flake.nix
··· 43 nixpkgs-lib.follows = "nixpkgs"; 44 nixpkgs-stable.url = "github:nixos/nixpkgs/release-25.05"; 45 systems.url = "github:nix-systems/default"; 46 }; 47 48 }
··· 43 nixpkgs-lib.follows = "nixpkgs"; 44 nixpkgs-stable.url = "github:nixos/nixpkgs/release-25.05"; 45 systems.url = "github:nix-systems/default"; 46 + theirs = { 47 + inputs = { 48 + den.follows = "den"; 49 + flake-aspects.follows = "flake-aspects"; 50 + flake-parts.follows = "flake-parts"; 51 + import-tree.follows = "import-tree"; 52 + nixpkgs.follows = "nixpkgs"; 53 + }; 54 + url = "path:./modules/_example/ci/_theirs"; 55 + }; 56 }; 57 58 }
+96
templates/examples/modules/_example/ci/_theirs/flake.lock
···
··· 1 + { 2 + "nodes": { 3 + "den": { 4 + "locked": { 5 + "lastModified": 1766081768, 6 + "narHash": "sha256-8Ea1DW3YZHifezfdEFHWEIpZBNKvEL+3iFOEcl3eFBU=", 7 + "owner": "vic", 8 + "repo": "den", 9 + "rev": "7271da18c60ab4d7c275ecaab480d29729f05d17", 10 + "type": "github" 11 + }, 12 + "original": { 13 + "owner": "vic", 14 + "repo": "den", 15 + "type": "github" 16 + } 17 + }, 18 + "flake-aspects": { 19 + "locked": { 20 + "lastModified": 1766081176, 21 + "narHash": "sha256-JrsuNSIEXPS3AiIxuWZw+sJ2Td6ni1OkqbW6mO/F4Rs=", 22 + "owner": "vic", 23 + "repo": "flake-aspects", 24 + "rev": "d0a226c84be2900d307aa1896e4e2c6e451844b2", 25 + "type": "github" 26 + }, 27 + "original": { 28 + "owner": "vic", 29 + "repo": "flake-aspects", 30 + "type": "github" 31 + } 32 + }, 33 + "flake-parts": { 34 + "inputs": { 35 + "nixpkgs-lib": [ 36 + "nixpkgs" 37 + ] 38 + }, 39 + "locked": { 40 + "lastModified": 1762980239, 41 + "narHash": "sha256-8oNVE8TrD19ulHinjaqONf9QWCKK+w4url56cdStMpM=", 42 + "owner": "hercules-ci", 43 + "repo": "flake-parts", 44 + "rev": "52a2caecc898d0b46b2b905f058ccc5081f842da", 45 + "type": "github" 46 + }, 47 + "original": { 48 + "owner": "hercules-ci", 49 + "repo": "flake-parts", 50 + "type": "github" 51 + } 52 + }, 53 + "import-tree": { 54 + "locked": { 55 + "lastModified": 1763263999, 56 + "narHash": "sha256-AZ4UkBJQKfaL9sX+/mzc1xBtcJk8hDQGkhjWX0Py5hU=", 57 + "owner": "vic", 58 + "repo": "import-tree", 59 + "rev": "058bd03ac818ea349946323ae3c2837b4cab7f22", 60 + "type": "github" 61 + }, 62 + "original": { 63 + "owner": "vic", 64 + "repo": "import-tree", 65 + "type": "github" 66 + } 67 + }, 68 + "nixpkgs": { 69 + "locked": { 70 + "lastModified": 1763464769, 71 + "narHash": "sha256-AJHrsT7VoeQzErpBRlLJM1SODcaayp0joAoEA35yiwM=", 72 + "owner": "nixos", 73 + "repo": "nixpkgs", 74 + "rev": "6f374686605df381de8541c072038472a5ea2e2d", 75 + "type": "github" 76 + }, 77 + "original": { 78 + "owner": "nixos", 79 + "ref": "nixpkgs-unstable", 80 + "repo": "nixpkgs", 81 + "type": "github" 82 + } 83 + }, 84 + "root": { 85 + "inputs": { 86 + "den": "den", 87 + "flake-aspects": "flake-aspects", 88 + "flake-parts": "flake-parts", 89 + "import-tree": "import-tree", 90 + "nixpkgs": "nixpkgs" 91 + } 92 + } 93 + }, 94 + "root": "root", 95 + "version": 7 96 + }
+13
templates/examples/modules/_example/ci/_theirs/flake.nix
···
··· 1 + { 2 + outputs = inputs: inputs.flake-parts.lib.mkFlake { inherit inputs; } (inputs.import-tree ./modules); 3 + 4 + inputs = { 5 + nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; 6 + flake-parts.url = "github:hercules-ci/flake-parts"; 7 + flake-parts.inputs.nixpkgs-lib.follows = "nixpkgs"; 8 + 9 + import-tree.url = "github:vic/import-tree"; 10 + flake-aspects.url = "github:vic/flake-aspects"; 11 + den.url = "github:vic/den"; 12 + }; 13 + }
+21
templates/examples/modules/_example/ci/_theirs/modules/theirs.nix
···
··· 1 + # This flake is for testing by ci/namespace.nix 2 + { inputs, ... }: 3 + { 4 + systems = [ 5 + "x86_64-linux" 6 + "aarch64-darwin" 7 + ]; 8 + imports = [ 9 + inputs.den.flakeModule 10 + (inputs.den.namespace "sim" true) 11 + ]; 12 + 13 + sim.a._.b._.c._.d = { 14 + nixos.sims = [ "theirs abcd" ]; 15 + }; 16 + 17 + sim.ul._.a._.tion = { 18 + nixos.sims = [ "theirs simulation" ]; 19 + }; 20 + 21 + }
+13 -1
templates/examples/modules/_example/ci/namespace.nix
··· 17 # enable <angle/bracket> syntax for finding aspects. 18 _module.args.__findFile = den.lib.__findFile; 19 20 imports = [ 21 # create a local namespace and output at flake.denful.eg 22 (inputs.den.namespace "eg" true) ··· 34 inputA 35 inputB 36 exposeToFlake 37 ] 38 ) 39 ]; ··· 71 "inputB simulation" 72 "local namespace" 73 "local simulation" 74 ]; 75 actual = lib.sort (a: b: a < b) rockhopper.config.sims; 76 in 77 - expected == actual 78 ); 79 80 };
··· 17 # enable <angle/bracket> syntax for finding aspects. 18 _module.args.__findFile = den.lib.__findFile; 19 20 + # example "external" flake to import some aspects from. 21 + flake-file.inputs.theirs = { 22 + url = "path:./modules/_example/ci/_theirs"; 23 + inputs.nixpkgs.follows = "nixpkgs"; 24 + inputs.flake-parts.follows = "flake-parts"; 25 + inputs.import-tree.follows = "import-tree"; 26 + inputs.flake-aspects.follows = "flake-aspects"; 27 + inputs.den.follows = "den"; 28 + }; 29 + 30 imports = [ 31 # create a local namespace and output at flake.denful.eg 32 (inputs.den.namespace "eg" true) ··· 44 inputA 45 inputB 46 exposeToFlake 47 + inputs.theirs # from actual external flake 48 ] 49 ) 50 ]; ··· 82 "inputB simulation" 83 "local namespace" 84 "local simulation" 85 + "theirs simulation" 86 ]; 87 actual = lib.sort (a: b: a < b) rockhopper.config.sims; 88 in 89 + expected == (builtins.trace actual actual) 90 ); 91 92 };