Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions docs/src/content/docs/guides/configure-aspects.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -185,3 +185,51 @@ Owned configs from `den.default` are deduplicated across pipeline stages.
Parametric functions in `den.default.includes` are evaluated at every context
stage. Use `den.lib.take.exactly` if a function should only run in specific contexts.
</Aside>


## Aspect Meta-Data

Aspects have a `meta` submodule that can be used to attach meta-data
at the aspect-level.

This is useful for introspection, for example, using some key to filter out aspects
or communicate information about aspects to other tooling like graph generators.

Since `.meta` is a freeformType you can add any custom attribute or import a module on it.

The following are default `meta.*` attributes defined by Den:

```nix
aspect.name # "den.aspects.igloo"
meta.loc # the location [ "den" "aspects" "igloo" ] from where name is derived.
meta.file # the first file where this aspect was defined.
meta.self # a reference to the aspect module `config`.
```

You can acess meta values by referencing an aspect:

```nix
den.aspects.igloo.meta.name
```

Or if an aspect needs to access its own meta data as part of its parametric functor,
it can do something like:


```nix
den.aspects.foo =
{ config, lib, ... }: # module args to access `config.meta`
{
meta.foo = "bar";
__functor = self: ctx:
if config.meta.foo == "bar"
then { } # do nothing, skip.
{
includes = filter (i: someCondition i) self.includes;
};
}

```



29 changes: 21 additions & 8 deletions nix/lib/aspects/resolve.nix
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,34 @@ let

inherit (den.lib) canTake take;

apply =
provided: args:
let
res = if canTake.upTo args provided then take.upTo provided args else provided;
mod =
den.lib.aspects.types.aspectType.merge
[ ]
[
{
file = "<curried>";
value = res;
}
];
in
if lib.isFunction res then mod else res;

resolve =
class: prev-chain: provided:
let
aspect = if canTake.upTo args provided then take.upTo provided args else provided;
aspect = apply provided { inherit class aspect-chain; };

aspect-chain = prev-chain ++ [ provided ] ++ (lib.optional (provided != aspect) aspect);

args = {
inherit aspect class aspect-chain;
};

imports =
(lib.optional (aspect ? ${class}) aspect.${class})
++ (map (resolve class aspect-chain) (aspect.includes or [ ]));
classModule = lib.optional (aspect ? ${class}) (
lib.setDefaultModuleLocation "${class}@${aspect.name}" aspect.${class}
);

imports = classModule ++ (map (resolve class aspect-chain) (aspect.includes or [ ]));
in
{
inherit imports;
Expand Down
40 changes: 35 additions & 5 deletions nix/lib/aspects/types.nix
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ let
};

isProviderFn = canTake.upTo {
aspect = true;
aspect-chain = true;
class = true;
};
Expand Down Expand Up @@ -48,9 +47,31 @@ let
let
sub = aspectSubmodule cnf;
in
sub
// {
merge = loc: defs: sub.merge loc defs;
sub // { merge = mergeWithAspectMeta sub; };

mergeWithAspectMeta =
sub: loc: defs:
sub.merge loc (
defs
++ [
{
file = (lib.head defs).file;
value = aspectMeta loc defs;
}
]
);

aspectMeta =
loc: defs:
{ config, ... }:
let
names = map (x: if builtins.isString x then x else "<anon>") loc;
nameFromLoc = lib.concatStringsSep "." names;
in
{
name = nameFromLoc;
meta.file = (lib.last defs).file;
meta.loc = loc;
};

aspectSubmodule =
Expand All @@ -59,7 +80,6 @@ let
{ name, config, ... }:
{
freeformType = lib.types.lazyAttrsOf lib.types.deferredModule;
config._module.args.aspect = config;
imports = [ (lib.mkAliasOptionModule [ "_" ] [ "provides" ]) ];

options = {
Expand All @@ -77,6 +97,16 @@ let
type = lib.types.str;
};

meta = lib.mkOption {
description = "Aspect attached meta data";
type = lib.types.submodule {
freeformType = lib.types.lazyAttrsOf lib.types.unspecified;
self = config;
};
defaultText = lib.literalExpression "{ }";
default = { };
};

includes = lib.mkOption {
description = "Providers to ask aspects from";
type = lib.types.listOf (providerType cnf);
Expand Down
118 changes: 118 additions & 0 deletions templates/ci/modules/features/aspect-meta.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
{ denTest, ... }:
{
flake.tests.aspect-meta = {

test-meta-can-be-referenced = denTest (
{ den, funnyNames, ... }:
{
den.aspects.foo = {
funny.names = [ den.aspects.foo.meta.nick ];
meta.nick = "McLoving";
};

expr = funnyNames den.aspects.foo;
expected = [ "McLoving" ];
}
);

test-meta-can-be-self-referenced = denTest (
{ den, funnyNames, ... }:
{
den.aspects.foo =
{ config, ... }:
{
funny.names = [ config.meta.nick ];
meta.nick = "McLoving";
};

expr = funnyNames den.aspects.foo;
expected = [ "McLoving" ];
}
);

test-name-can-be-referenced = denTest (
{ den, funnyNames, ... }:
{
den.aspects.foo = {
funny.names = [ den.aspects.foo.name ];
};

expr = funnyNames den.aspects.foo;
expected = [ "den.aspects.foo" ];
}
);

test-name-can-be-self-referenced = denTest (
{ den, funnyNames, ... }:
{
den.aspects.foo =
{ config, ... }:
{
funny.names = [ config.name ];
};

expr = funnyNames den.aspects.foo;
expected = [ "den.aspects.foo" ];
}
);

test-meta-keys-at-host-aspect = denTest (
{
den,
igloo,
lib,
...
}:
{
den.hosts.x86_64-linux.igloo = { };
den.aspects.igloo.includes = [ den.aspects.foo ];

den.aspects.foo =
{ host }:
{
nixos.environment.sessionVariables = {
KEYS = lib.attrNames den.aspects.foo.meta.self.meta;
};
};

expr = {
inherit (igloo.environment.sessionVariables)
KEYS
;
};
expected.KEYS = "file:loc:self";
}
);

test-meta-keys-at-host-fixpoint = denTest (
{
den,
igloo,
lib,
...
}:
{
den.hosts.x86_64-linux.igloo = { };
den.aspects.igloo.includes = [ den.aspects.foo ];

den.aspects.foo =
{ host }:
{ config, lib, ... }:
{
nixos.environment.sessionVariables = {
KEYS = lib.attrNames config.meta;
};
meta.foo = 12;
};

expr = {
inherit (igloo.environment.sessionVariables)
KEYS
;
};
expected.KEYS = "file:foo:loc:self";
}
);

};
}
Loading