tangled
alpha
login
or
join now
quasigod.xyz
/
den
forked from
oeiuwq.com/den
Modular, context-aware and aspect-oriented dendritic Nix configurations. Discussions: https://oeiuwq.zulipchat.com/join/nqp26cd4kngon6mo3ncgnuap/
0
fork
atom
overview
issues
pulls
pipelines
Compare changes
Choose any two refs to compare.
base:
theirs
push-nvzsmnpkpzry
main
v0.7.0
v0.6.0
v0.5.0
v0.4.0
v0.3.0
v0.2.0
v0.1.0
compare:
theirs
push-nvzsmnpkpzry
main
v0.7.0
v0.6.0
v0.5.0
v0.4.0
v0.3.0
v0.2.0
v0.1.0
go
+237
-12
8 changed files
expand all
collapse all
unified
split
.github
workflows
test.yml
nix
namespace.nix
templates
examples
flake.lock
flake.nix
modules
_example
ci
_theirs
flake.lock
flake.nix
modules
theirs.nix
namespace.nix
+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);
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
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};
0
0
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"
0
255
}
256
},
257
"systems": {
···
268
"repo": "default",
269
"type": "github"
270
}
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
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";
0
0
0
0
0
0
0
0
0
0
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
···
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
···
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
···
0
0
0
0
0
0
0
0
0
0
0
0
0
···
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
···
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
···
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
0
0
0
0
0
0
0
0
0
0
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
0
37
]
38
)
39
];
···
71
"inputB simulation"
72
"local namespace"
73
"local simulation"
0
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
};