Skip to content

Commit 8659af2

Browse files
committed
feat(pipeline): simple routes collect from scope subtree
Simple routes (to-packages, to-apps, etc.) previously collected class content only from their own scope. Parametric class keys like `packages = { host }: { pkgs, ... }: ...` resolve at entity scopes (host/user/home) which are descendants of the route's scope, so the route never saw them. Change applySimpleRoute to walk the scopeParent tree and collect from the source scope's entire subtree. This works with any topology — default (flake-system → host → user), fleet (fleet → environment → host), or custom — because the collection follows the scope hierarchy rather than hardcoding a specific path. Adds test: parametric packages from user includes across two systems.
1 parent 33543ec commit 8659af2

3 files changed

Lines changed: 69 additions & 8 deletions

File tree

nix/lib/aspects/fx/resolve.nix

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -148,11 +148,12 @@ let
148148

149149
# Phase 3: Apply routes.
150150
applyRoutes =
151-
fxResolve: ctx: scopeContexts: rootScopeId: scopedRoutes: acc:
151+
fxResolve: ctx: scopeContexts: rootScopeId: scopeParent: scopedRoutes: acc:
152152
route.applyRoutes {
153153
inherit
154154
scopedRoutes
155155
scopeContexts
156+
scopeParent
156157
ctx
157158
rootScopeId
158159
fxResolve
@@ -325,7 +326,7 @@ let
325326
subtreePhase1 = wrapPerScope ctx subtreeContexts subtreeClassImports;
326327
subtreePhase2 = applyProvides ctx relevantContexts subtreeProvides subtreePhase1;
327328
subtreePhase3 =
328-
applyRoutes fxResolveFn ctx relevantContexts hostScopeId subtreeRoutes
329+
applyRoutes fxResolveFn ctx relevantContexts hostScopeId scopeParent subtreeRoutes
329330
subtreePhase2;
330331
in
331332
extractSubtreeModules subtreePhase3.perScope scopeParent hostScopeId hostClass
@@ -444,7 +445,7 @@ let
444445
subtreePhase1 = wrapPerScope ctx subtreeContexts subtreeClassImports;
445446
subtreePhase2 = applyProvides ctx relevantContexts subtreeProvides subtreePhase1;
446447
subtreePhase3 =
447-
applyRoutes (fxResolve mkPipeline) ctx relevantContexts hostScopeId subtreeRoutes
448+
applyRoutes (fxResolve mkPipeline) ctx relevantContexts hostScopeId scopeParent subtreeRoutes
448449
subtreePhase2;
449450
preWalkedModules = extractSubtreeModules subtreePhase3.perScope scopeParent hostScopeId hostClass;
450451
modules = if preWalkedModules != null then preWalkedModules else [ spec.mainModule ];
@@ -569,7 +570,8 @@ let
569570
phase1 = wrapPerScope ctx augmentedScopeContexts drainedClassImportsRaw;
570571
phase2 = applyProvides ctx augmentedScopeContexts scopedProvides phase1;
571572
phase3 =
572-
applyRoutes (fxResolve mkPipeline) ctx augmentedScopeContexts result.state.rootScopeId scopedRoutes
573+
applyRoutes (fxResolve mkPipeline) ctx augmentedScopeContexts result.state.rootScopeId scopeParent
574+
scopedRoutes
573575
phase2;
574576
phase4 = applyInstantiates {
575577
scopedInstantiates = result.state.scopedInstantiates null;
@@ -618,6 +620,7 @@ let
618620
phase2 = applyProvides ctx augmentedScopeContexts (result.state.scopedProvides null) phase1;
619621
phase3 =
620622
applyRoutes (fxResolveImports mkPipeline) ctx augmentedScopeContexts result.state.rootScopeId
623+
(result.state.scopeParent null)
621624
(result.state.scopedRoutes null)
622625
phase2;
623626
in

nix/lib/aspects/fx/route/apply.nix

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -111,16 +111,33 @@ let
111111
};
112112
};
113113

114+
# Collect class modules from a scope and all its descendants.
115+
collectFromSubtree =
116+
wrappedPerScope: scopeParent: rootScopeId: fromClass:
117+
let
118+
allScopeIds = builtins.attrNames wrappedPerScope;
119+
isInSubtree =
120+
sid:
121+
sid == rootScopeId
122+
|| (
123+
let
124+
parent = scopeParent.${sid} or null;
125+
in
126+
parent != null && parent != sid && isInSubtree parent
127+
);
128+
subtreeScopes = builtins.filter isInSubtree allScopeIds;
129+
in
130+
lib.concatMap (sid: wrappedPerScope.${sid}.${fromClass} or [ ]) subtreeScopes;
131+
114132
applySimpleRoute =
115133
acc:
116134
{
117135
route,
118136
wrappedPerScope,
137+
scopeParent,
119138
}:
120139
let
121-
scopeExists = wrappedPerScope ? ${route.sourceScopeId};
122-
sourceModules =
123-
if !scopeExists then [ ] else wrappedPerScope.${route.sourceScopeId}.${route.fromClass} or [ ];
140+
sourceModules = collectFromSubtree wrappedPerScope scopeParent route.sourceScopeId route.fromClass;
124141
adapterMod = route.adapterModule or null;
125142
modulesWithAdapter = if adapterMod == null then sourceModules else sourceModules ++ [ adapterMod ];
126143
isFlakeRoute = route.intoClass == "flake";
@@ -207,6 +224,7 @@ let
207224
scopedRoutes,
208225
wrappedPerScope,
209226
classImports,
227+
scopeParent ? { },
210228
scopeContexts ? { },
211229
ctx ? { },
212230
fxResolve ? null,
@@ -232,7 +250,7 @@ let
232250
;
233251
}
234252
else
235-
applySimpleRoute acc { inherit route wrappedPerScope; }
253+
applySimpleRoute acc { inherit route wrappedPerScope scopeParent; }
236254
)
237255
{
238256
inherit classImports;

templates/ci/modules/features/forward-flake-level.nix

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,46 @@
160160
}
161161
);
162162

163+
# Parametric packages from user includes roll up to flake output.
164+
# The { host }: wrapper resolves per-host at user scope; the
165+
# to-packages route collects from the flake-system subtree.
166+
test-parametric-packages-from-user-includes = denTest (
167+
{
168+
den,
169+
lib,
170+
config,
171+
inputs,
172+
...
173+
}:
174+
{
175+
imports = [ inputs.den.flakeOutputs.packages ];
176+
177+
den.hosts.x86_64-linux.alpha.users.a = { };
178+
den.hosts.aarch64-linux.beta.users.b = { };
179+
180+
den.aspects.a.includes = [ den.aspects.nh-tool ];
181+
den.aspects.b.includes = [ den.aspects.nh-tool ];
182+
183+
den.aspects.nh-tool = {
184+
packages =
185+
{ host }:
186+
{ pkgs, ... }:
187+
{
188+
"sw-${host.name}" = pkgs.writeText "sw-${host.name}" host.name;
189+
};
190+
};
191+
192+
expr = {
193+
alpha = config.flake.packages.x86_64-linux ? sw-alpha;
194+
beta = config.flake.packages.aarch64-linux ? sw-beta;
195+
};
196+
expected = {
197+
alpha = true;
198+
beta = true;
199+
};
200+
}
201+
);
202+
163203
test-route-flake-outputs-from-hosts = denTest (
164204
{
165205
den,

0 commit comments

Comments
 (0)