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
1 change: 1 addition & 0 deletions modules/aspect-schema.nix
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ let
"schema"
"classes"
"_module"
"_"
];
nsNames = builtins.attrNames (config.den.ful or { });
nsCollected = map (
Expand Down
31 changes: 14 additions & 17 deletions modules/aspects/batteries/host-aspects.nix
Original file line number Diff line number Diff line change
Expand Up @@ -14,28 +14,25 @@ let
specifically for `user.classes`.
'';

# Emit a deferred node spawn request. Resolution happens post-walk (in
# resolve.nix's drain augmentation) where the parent scope-tree state (host +
# siblings) exists, so the projection sees the fleet — a host-aspects-projected
# homeManager consumer of a fleet-collected pipe lists every peer. Ancestor
# bindings like `environment` arrive via the threaded scope context, not
# manual chainCtx threading.
from-host =
{ host, user }:
let
# Tag host.aspect with user context so parametric includes like
# { user }: ... can resolve during host-aspects re-resolution.
ctx = { inherit host user; };
scopeHandlers = den.lib.aspects.fx.handlers.constantHandler ctx;
aspectWithCtx = host.aspect // {
__scopeHandlers = scopeHandlers;
};
in
{
name = "host-aspects/${user.userName}@${host.name}";
}
// lib.genAttrs (user.classes or [ "homeManager" ]) (
class: den.lib.aspects.resolveImports class aspectWithCtx
);
{ host, user, ... }: [ (den.lib.policy.spawn { classes = user.classes or [ "homeManager" ]; }) ];
in
{
den.batteries.host-aspects = {
name = "host-aspects";
inherit description;
includes = [ from-host ];
includes = [
{
__isPolicy = true;
name = "host-aspects-project";
fn = from-host;
}
];
};
}
13 changes: 11 additions & 2 deletions nix/lib/aspects/fx/aspect/normalize.nix
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,16 @@ let
# into includes so the pipeline resolves them. listOf doesn't call
# providerType.merge per-element, so inner wrappers in includes lists
# arrive here unprocessed.
else if builtins.isAttrs child && child ? __contentValues && !(child ? name) then
# A navigated nested aspect carries __provider (its full path) but may have
# no __contentValues (single-def keys forward their raw value directly).
# Either way, when it has no name yet, derive name + meta.provider from
# __provider so it resolves to its OWN identity (e.g. apps/gaming/steam)
# regardless of inclusion path. Without this it falls through nameless and
# children.nix renames it to <parent>/<anon>:<idx>, so the same aspect
# included via two paths gets two identities and fails to dedup.
else if
builtins.isAttrs child && (child ? __contentValues || child ? __provider) && !(child ? name)
then
let
prov = child.__provider or [ ];
provName = if prov != [ ] then lib.last prov else null;
Expand All @@ -98,7 +107,7 @@ let
in
args != { } && !(args ? config) && !(args ? options)
)
) child.__contentValues;
) (child.__contentValues or [ ]);
in
child
// lib.optionalAttrs (provName != null) {
Expand Down
Loading
Loading