Skip to content
Draft
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 nix/lib/can-take.nix
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@ in
{
__functor = self: self.atLeast;
atLeast = params: func: (canTake params func).satisfied;
upTo = params: func: (canTake params func).satisfied;
exactly = params: func: (canTake params func).exactly;
}
2 changes: 2 additions & 0 deletions nix/lib/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ let
ctxApply = ./ctx-apply.nix;
ctxTypes = ./ctx-types.nix;
__findFile = ./den-brackets.nix;
recursiveFunctor = ./recursive-functor.nix;
fwTypes = ./types.nix;
forward = ./forward.nix;
home-env = ./home-env.nix;
nh = ./nh.nix;
Expand Down
8 changes: 6 additions & 2 deletions nix/lib/parametric.nix
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{ lib, den, ... }:
let
inherit (den.lib) take;
inherit (den.lib) take recursiveFunctor;
inherit (den.lib.statics) owned statics isCtxStatic;

parametric.applyIncludes =
Expand Down Expand Up @@ -44,7 +44,11 @@ let
};
};

parametric.fixedTo = parametric.deep parametric.atLeast;
parametric.fixedTo = parametric.deep parametric.atLeast // {
atLeast = recursiveFunctor (lib.flip take.atLeast);
exactly = recursiveFunctor (lib.flip take.exactly);
upTo = recursiveFunctor (lib.flip take.upTo);
};

parametric.withOwn =
functor: aspect:
Expand Down
21 changes: 21 additions & 0 deletions nix/lib/recursive-functor.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# "Just Give 'Em One of These" - Moe Szyslak
# A __functor that applies context to parametric includes (functions) and recurses into other included aspects
{ lib, ... }:
let
recursiveApply =
apply: ctx: include:
if include ? includes then recursiveFunctor apply include ctx else apply ctx include;
recursiveFunctor =
apply: aspect:
aspect
// {
__functor = self: ctx: {
includes =
self.includes or [ ]
|> builtins.filter lib.isFunction
|> map (recursiveApply apply ctx)
|> builtins.filter (x: x != { });
};
};
in
recursiveFunctor
11 changes: 6 additions & 5 deletions nix/lib/take.nix
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
{ den, ... }:
{ den, lib, ... }:
let
take.unused = _unused: used: used;
take.exactly = take den.lib.canTake.exactly;
take.atLeast = take den.lib.canTake.atLeast;
take.exactly = take (_fn: ctx: ctx) den.lib.canTake.exactly;
take.atLeast = take (_fn: ctx: ctx) den.lib.canTake.atLeast;
take.upTo = take (fn: fn |> lib.functionsArgs |> builtins.intersectAttrs) den.lib.canTake.upTo;
take.__functor =
_: takes: fn: ctx:
if takes ctx fn then fn ctx else { };
_: takes: adapter: fn: ctx:
if takes ctx fn then fn (adapter fn ctx) else { };
in
take
177 changes: 170 additions & 7 deletions templates/ci/modules/features/parametric.nix
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
{ denTest, ... }:
{
flake.tests.parametric = {

test-parametric-forwards-context = denTest (
{ den, igloo, ... }:
{
den,
igloo,
...
}:
let
foo = den.lib.parametric {
includes = [
Expand All @@ -26,7 +29,11 @@
);

test-parametric-owned-config = denTest (
{ den, igloo, ... }:
{
den,
igloo,
...
}:
let
foo = den.lib.parametric {
nixos.networking.hostName = "from-parametric-owned";
Expand All @@ -43,7 +50,11 @@
);

test-parametric-fixedTo = denTest (
{ den, igloo, ... }:
{
den,
igloo,
...
}:
let
foo =
{ host, ... }:
Expand All @@ -68,12 +79,20 @@
);

test-parametric-expands = denTest (
{ den, igloo, ... }:
{
den,
igloo,
...
}:
let
foo = den.lib.parametric.expands { planet = "Earth"; } {
includes = [
(
{ host, planet, ... }:
{
host,
planet,
...
}:
{
nixos.users.users.tux.description = "${host.name}/${planet}";
}
Expand All @@ -91,7 +110,11 @@
);

test-never-matches-aspect-skipped = denTest (
{ den, igloo, ... }:
{
den,
igloo,
...
}:
let
never-matches =
{ never-exists, ... }:
Expand All @@ -113,5 +136,145 @@
}
);

test-parametric-fixedTo-atLeast = denTest (
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a giant test of all the new functionality, I would need to split this apart.

{
den,
lib,
inputs,
...
}:
let
inherit (den.lib.parametric) fixedTo;
testAspect = name: include: {
nixos.test = [ "excluded-owned-${name}" ];

_.host =
{ host }:
{
nixos.test = [ "${host}-${name}" ];
};

_.host-user =
{ host, user }:
{
nixos.test = [ "${host}-${user}-${name}" ];
};

_.static =
{ class, ... }:
{
${class}.test = [ "excluded-static-${name}" ];
};

includes = include ++ [
den.aspects.${name}._.host
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I belive there's a fixed point for aspects: { aspect,...} see lib/aspects/types.nix so you don't have to access by name, aspect can reference itself, just an option.

den.aspects.${name}._.host-user
den.aspects.${name}._.static
];
};
testOptionProvider = args: aspect: {
includes = [
aspect
{
__functor = self: _: {
nixos.options.test = lib.mkOption { type = lib.types.listOf lib.types.str; };
};
__functionArgs =
args
|> map (arg: {
name = arg;
value = false;
})
|> builtins.listToAttrs;
}
];
};
in
{
den.aspects.inner = testAspect "inner" [ ];
den.aspects.outer = testAspect "outer" [ den.aspects.inner ];

expr =
{
exactlyHost = {
ctx = {
host = "igloo";
};
functor = fixedTo.exactly;
};
exactlyHostUser = {
ctx = {
host = "igloo";
user = "tux";
};
functor = fixedTo.exactly;
};
upToHost = {
ctx = {
host = "igloo";
};
functor = fixedTo.upTo;
};
upToHostUser = {
ctx = {
host = "igloo";
user = "tux";
};
functor = fixedTo.upTo;
};
atLeastHost = {
ctx = {
host = "igloo";
};
functor = fixedTo.atLeast;
};
# This test case errors because atLeast tries to call { host }: with { host, user } causing an error
# this is IMO incorrect behaviour, but would technically be a breaking change if people are using
# args@{ host, ... } which is why I introduced a new kind "upTo" which uses the canTake.atLeast
# predicate but only calls the function with the attributes it expects
# atLeastHostUser = {
# ctx = {
# host = "igloo";
# user = "tux";
# };
# parametricFunctor = atLeast.fixed;
# };
}
|> lib.mapAttrs (
_: test:
den.aspects.outer
|> testOptionProvider (builtins.attrNames test.ctx)
|> (lib.flip test.functor) test.ctx
|> den.lib.aspects.resolve "nixos" [ ]
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this api changed when we bundled flake aspects, no need for the list at the end

|> (nixos: inputs.nixpkgs.lib.evalModules { modules = [ nixos ]; })
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how about defining options.test here, since it is used for checking values. instead of defining the option at an aspect.

|> (x: x.config.test)
);
Comment on lines +243 to +251
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I was working on this, I wanted to stay away from ctx.host and den.host because they seemed the most magical to me at the time and I wanted to ensure if I hit any problems it was with this specifically, so there's some machinery here that would normally be handled by den.host. Should be fairly simple to switch it over to be consistent with the rest of the tests.


expected = {
atLeastHost = [
"igloo-inner"
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to have these assertions separated. you can still use a factory function that wraps denTest, the reason is we better have independent tests that can give us clues of what to fix in the future.

"igloo-outer"
];
exactlyHost = [
"igloo-inner"
"igloo-outer"
];
exactlyHostUser = [
"igloo-tux-inner"
"igloo-tux-outer"
];
upToHost = [
"igloo-inner"
"igloo-outer"
];
upToHostUser = [
"igloo-tux-inner"
"igloo-inner"
"igloo-tux-outer"
"igloo-outer"
];
};
}
);
};
}