commits
flake-file's dendritic module no longer includes den nor flake-aspects. We now provide our own flakeModules.dendritic that does so.
Fixes #59
See https://github.com/vic/den/discussions/99#discussion-9150825
Fixes #99
This introduces `parametric.withOwn` that is just a `parametric.atLeast`
that also includes the aspect owned configs.
See discussion:
https://github.com/vic/den/discussions/91#discussioncomment-14985716
Calling `parametric x` itself is an alias for `parametric.withOwn x`.
So that both syntax do the same:
```
den.aspects.foo = {
__functor = den.lib.parametric;
nixos.foo = 1;
includes = [ bar ];
};
```
and also, **this is the preferred* syntax that will be documented, since
it is more intuitive and does not surfaces `__functor`:
```
den.aspects.foo = den.lib.parametric {
nixos.foo = 1;
includes = [ bar ];
};
```
Still have to update documentation about this combinator.
Fixes #97.
See https://github.com/vic/den/discussions/91 for context.
See
[failure](https://github.com/vic/bugs-den/actions/runs/19377336327/job/55448108522)
for
[reproduction](https://github.com/vic/bugs-den/commit/f81dca61403c7a08e5381b61bcb2fedc1994efb4)
Support for top-level contextual aspects was added at
https://github.com/vic/flake-aspects/pull/14.
Closes #92.
Fixes #87
Edit `modules/vm.nix`
Closes #82
to be more precise about it providing a fixed context to all its
included functions.
Fixes #84.
This PR removes the directional contexts `fromHost` and `fromUser`. Now
people can include funcitons like:
```nix
den.default.includes = [
({ user, ...}: { nixos.some-array = [ user.name ]; })
];
```
And values are not duplicated now. Added test based on report from #84.
This also simplified a lot the number of contexts we have. Still have to
update documentation about contexts.
This also removes outdated `_profile` directory. Closes #78. Instead our
default template now includes an `eg/routes.nix` example router and
exercises namespaces and angle-brackets, ensuring via tests that they
work.
split into several files, more dendritic.
This helps people find aspect implementation, aspect usage and test in one file.
Closes #73
Available at https://vic.github.io/den
Closes #66. Partially, still need to split README into several pages.
Our new default template is much better suited to be used as a starting
point for people trying out den.
Our previous default template is now named `examples`. It is still
useful as reference of how to use den and is used for CI.
Fixes #67
( Closes #56, #55 ) -- (Pending #58)
This PR introduces a fundamental refactoring of the aspect dependency and context management system. The primary goal is to provide a more robust, explicit, and powerful way to handle how different parts of the configuration (hosts, users, home-manager) interact with each other. This leads to clearer and more predictable evaluations, but also introduces significant breaking changes from previous unified context `userToHost` and `hostToUser`.
> [!IMPORTANT]
> The previous context objects `userToHost` and `hostToUser` prove to be harder to use and have been **completely removed**.
Now contexts are incremental and context now are used for **configuration intent**. For example, a NixOS configuration starts with context `{ OS }` but later expands to `{ OS, user, host }` as soon as the OS has information of which users and hosts it is being applied to. See the examples on aspects.nix for how this replaces `userToHost` and `hostToUser`.
> [!IMPORTANT]
> Since contexts are now _incremental_, they are also no longer _exact_ by default.
What this means: In previous releases, a function `{ host }: { }` context had to match exactly. For example if applied to `{ host = ...; foo = ... }` it would not apply since `foo` is unexpected. This was as to avoid `function called with unexpectd "foo"` errors. However now contexts can be incremental.
A function `{ host, ... }: { }` provides anytime a `host` is in context.
A function `{ host, user, ... }: { }` provides when at least both are in context.
For example, you can use `{ user, ... }: ...` to provide configurations based on user data anytime it is in context.
**OS/HM Intent Contexts**
OS and HM configurations now start with a single `{ OS }` and `{ HM }` contexts. They are higher-level contexts used to describe the **Intent** of the configuration being produced. This, in hand with incremental contexts, allows to get rid of `hostToUser` and `userToHost`.
For example a context `{ OS, user, host }` is explicit about it being a configuration depending on host/user data but also that it is **being used** for providing NixOS/Darwin configuration specifically.
`OS`/`HM` are not used in our provided batteries at all. Instead the batteries rely only on `host`/`user`/`home` data being in context, fixing the complexity that was introduced by previous `hostToUser` and `userToHost`.
`OS`/`HM` are useful for opt-in bi-directional dependencies, this is how we avoid duplicating settings now. Since a function `{ user, host, ...}: { }` can be installed in `den.default`, and `den.default` is included for any User and Host. That function would produce duplicate configurations - this is correct since the function is **explicitly set in den.default** which is included twice: once for the user and once for the host. To avoid this, either install the function on a specific `den.aspect.<host/user>.includes` or use a higher **intent context** like `OS`/`HM`. See how our aspects.nix examples were updated for this.
## refactor: Dependency Resolution Engine
The core dependency resolution logic in `modules/aspects/dependencies.nix` has been rewritten to be more explicit and prevent recursion.
- It now uses `take.exactly` with the new `OS` and `HM` contexts to ensure that dependency-gathering functions run only once at the appropriate level, preventing evaluation loops.
- The logic for how users contribute to a host's configuration (`osIncludesFromUsers`) and how a host contributes to a user's home-manager configuration (`hmIncludesFromHost`) is now clearly defined and separated.
- The new engine correctly passes the intent `OS` and `HM` contexts down to the aspects being resolved.
## refactor: `provides` Modules Updated for New Context
All built-in `provides` modules have been updated to use the new intent-based context system.
- `provides/define-user`: Now uses generic data-specific `{ host, user, ... }` and `{ home, ... }` contexts directly, removing the need for the intermediate `userToHostContext`.
- `provides/home-manager`: Now passes the `{ HM }` **intent** context when resolving a user's `homeManager` aspect.
- `provides/primary-user`: Logic simplified to expect `{ user, host, ... }` instead of `userToHost`.
- `provides/user-shell`: The logic has been unified. A single `userShell` function now correctly applies shell configuration to `nixos`, `darwin`, and `homeManager` attributes based on the context it receives (`{ user, ... }` or `{ home, ... }`).
## test: Enhanced `canTake` Tests
The test suite for `function_can_take.nix` has been expanded to include assertions for the new `takes.exactly` functionality, ensuring that it correctly identifies functions that take an exact set of arguments.
## feat: Granular Context Control with `take` and `parametric`
A new set of library functions, `den.lib.take` and a redesigned `den.lib.parametric`, have been introduced to give aspect authors fine-grained control over how and when their functions are applied.
- **`den.lib.take`**: This is a new functor that allows a function to declare precisely what context it expects.
- `take.atLeast`: The function is applied if the context provides *at least* the required arguments. This is the default behavior for most parametric functions.
- `take.exactly`: The function is applied only if the context provides the *exact* set of required arguments, with no extras. This is crucial for preventing infinite recursion in dependency resolution.
- `take.unused`: A utility to signal which arguments from the context are "consumed" by the function, allowing for clearer composition.
- **`den.lib.parametric`**: The `parametric` functor has been completely overhauled to leverage `take`.
- It now offers `atLeast` and `exactly` variants to create aspects that apply their includes based on these context matching rules.
- The syntax `parametric true` is now an alias for `parametric.atLeast`.
This new system replaces the older, less precise `canTake` logic, providing a more declarative and safer way to write parametric aspects.
## BREAKING CHANGE: Intent Context (`OS` and `HM`)
The context passed to parametric aspects is no longer a flat attribute set. It has been restructured into namespaced contexts to clearly separate concerns between operating system configuration and home-manager configuration.
- **`OS` Context**: When evaluating for a host's operating system configuration (NixOS or `nix-darwin`), aspects now receive an `OS` attribute in their context. The original `host` object is available as `OS.host`.
- **`HM` Context**: When evaluating for a user's `home-manager` configuration, aspects receive an `HM` attribute. This context contains both the `user` and the `host` they belong to (`HM.user` and `HM.host`).
The previous context objects `userToHost` and `hostToUser` proven to be harder to use and have been **completely removed**. And now contexts are incremental. For example, a NixOS configuration starts with context `{ OS }` but later expands to `{ OS, user, host }` as soon as the OS has information of which users and hosts it is being applied to. See the examples on aspects.nix for how this replaces `userToHost` and `hostToUser`.
### Migration Guide
All parametric aspects must be updated to expect the new context structure.
**Before:**
```nix
my-aspect = { userToHost, ... }: {
${userToHost.host.class}.services.foo.enable = true;
};
my-other-aspect = { hostToUser, ... }: {
homeManager.programs.bar.enable = true;
};
```
**After:**
```nix
# OS context is needed only if my-aspect is installed at den.default
my-aspect = { OS, user, host, ... }: {
${host.class}.services.foo.enable = true;
};
# HM intent context is needed only if installed into den.default.
my-other-aspect = { HM, user, host, ... }: {
homeManager.programs.bar.enable = true;
};
```
Instead of previous: `{ fromUser, toHost }` and `{ fromHost, toUser }`.
The reason is detailed at #47.
Closes #47.
This introduces `parametric.withOwn` that is just a `parametric.atLeast`
that also includes the aspect owned configs.
See discussion:
https://github.com/vic/den/discussions/91#discussioncomment-14985716
Calling `parametric x` itself is an alias for `parametric.withOwn x`.
So that both syntax do the same:
```
den.aspects.foo = {
__functor = den.lib.parametric;
nixos.foo = 1;
includes = [ bar ];
};
```
and also, **this is the preferred* syntax that will be documented, since
it is more intuitive and does not surfaces `__functor`:
```
den.aspects.foo = den.lib.parametric {
nixos.foo = 1;
includes = [ bar ];
};
```
Still have to update documentation about this combinator.
Fixes #97.
See https://github.com/vic/den/discussions/91 for context.
See
[failure](https://github.com/vic/bugs-den/actions/runs/19377336327/job/55448108522)
for
[reproduction](https://github.com/vic/bugs-den/commit/f81dca61403c7a08e5381b61bcb2fedc1994efb4)
Support for top-level contextual aspects was added at
https://github.com/vic/flake-aspects/pull/14.
Closes #92.
Fixes #84.
This PR removes the directional contexts `fromHost` and `fromUser`. Now
people can include funcitons like:
```nix
den.default.includes = [
({ user, ...}: { nixos.some-array = [ user.name ]; })
];
```
And values are not duplicated now. Added test based on report from #84.
This also simplified a lot the number of contexts we have. Still have to
update documentation about contexts.
( Closes #56, #55 ) -- (Pending #58)
This PR introduces a fundamental refactoring of the aspect dependency and context management system. The primary goal is to provide a more robust, explicit, and powerful way to handle how different parts of the configuration (hosts, users, home-manager) interact with each other. This leads to clearer and more predictable evaluations, but also introduces significant breaking changes from previous unified context `userToHost` and `hostToUser`.
> [!IMPORTANT]
> The previous context objects `userToHost` and `hostToUser` prove to be harder to use and have been **completely removed**.
Now contexts are incremental and context now are used for **configuration intent**. For example, a NixOS configuration starts with context `{ OS }` but later expands to `{ OS, user, host }` as soon as the OS has information of which users and hosts it is being applied to. See the examples on aspects.nix for how this replaces `userToHost` and `hostToUser`.
> [!IMPORTANT]
> Since contexts are now _incremental_, they are also no longer _exact_ by default.
What this means: In previous releases, a function `{ host }: { }` context had to match exactly. For example if applied to `{ host = ...; foo = ... }` it would not apply since `foo` is unexpected. This was as to avoid `function called with unexpectd "foo"` errors. However now contexts can be incremental.
A function `{ host, ... }: { }` provides anytime a `host` is in context.
A function `{ host, user, ... }: { }` provides when at least both are in context.
For example, you can use `{ user, ... }: ...` to provide configurations based on user data anytime it is in context.
**OS/HM Intent Contexts**
OS and HM configurations now start with a single `{ OS }` and `{ HM }` contexts. They are higher-level contexts used to describe the **Intent** of the configuration being produced. This, in hand with incremental contexts, allows to get rid of `hostToUser` and `userToHost`.
For example a context `{ OS, user, host }` is explicit about it being a configuration depending on host/user data but also that it is **being used** for providing NixOS/Darwin configuration specifically.
`OS`/`HM` are not used in our provided batteries at all. Instead the batteries rely only on `host`/`user`/`home` data being in context, fixing the complexity that was introduced by previous `hostToUser` and `userToHost`.
`OS`/`HM` are useful for opt-in bi-directional dependencies, this is how we avoid duplicating settings now. Since a function `{ user, host, ...}: { }` can be installed in `den.default`, and `den.default` is included for any User and Host. That function would produce duplicate configurations - this is correct since the function is **explicitly set in den.default** which is included twice: once for the user and once for the host. To avoid this, either install the function on a specific `den.aspect.<host/user>.includes` or use a higher **intent context** like `OS`/`HM`. See how our aspects.nix examples were updated for this.
## refactor: Dependency Resolution Engine
The core dependency resolution logic in `modules/aspects/dependencies.nix` has been rewritten to be more explicit and prevent recursion.
- It now uses `take.exactly` with the new `OS` and `HM` contexts to ensure that dependency-gathering functions run only once at the appropriate level, preventing evaluation loops.
- The logic for how users contribute to a host's configuration (`osIncludesFromUsers`) and how a host contributes to a user's home-manager configuration (`hmIncludesFromHost`) is now clearly defined and separated.
- The new engine correctly passes the intent `OS` and `HM` contexts down to the aspects being resolved.
## refactor: `provides` Modules Updated for New Context
All built-in `provides` modules have been updated to use the new intent-based context system.
- `provides/define-user`: Now uses generic data-specific `{ host, user, ... }` and `{ home, ... }` contexts directly, removing the need for the intermediate `userToHostContext`.
- `provides/home-manager`: Now passes the `{ HM }` **intent** context when resolving a user's `homeManager` aspect.
- `provides/primary-user`: Logic simplified to expect `{ user, host, ... }` instead of `userToHost`.
- `provides/user-shell`: The logic has been unified. A single `userShell` function now correctly applies shell configuration to `nixos`, `darwin`, and `homeManager` attributes based on the context it receives (`{ user, ... }` or `{ home, ... }`).
## test: Enhanced `canTake` Tests
The test suite for `function_can_take.nix` has been expanded to include assertions for the new `takes.exactly` functionality, ensuring that it correctly identifies functions that take an exact set of arguments.
## feat: Granular Context Control with `take` and `parametric`
A new set of library functions, `den.lib.take` and a redesigned `den.lib.parametric`, have been introduced to give aspect authors fine-grained control over how and when their functions are applied.
- **`den.lib.take`**: This is a new functor that allows a function to declare precisely what context it expects.
- `take.atLeast`: The function is applied if the context provides *at least* the required arguments. This is the default behavior for most parametric functions.
- `take.exactly`: The function is applied only if the context provides the *exact* set of required arguments, with no extras. This is crucial for preventing infinite recursion in dependency resolution.
- `take.unused`: A utility to signal which arguments from the context are "consumed" by the function, allowing for clearer composition.
- **`den.lib.parametric`**: The `parametric` functor has been completely overhauled to leverage `take`.
- It now offers `atLeast` and `exactly` variants to create aspects that apply their includes based on these context matching rules.
- The syntax `parametric true` is now an alias for `parametric.atLeast`.
This new system replaces the older, less precise `canTake` logic, providing a more declarative and safer way to write parametric aspects.
## BREAKING CHANGE: Intent Context (`OS` and `HM`)
The context passed to parametric aspects is no longer a flat attribute set. It has been restructured into namespaced contexts to clearly separate concerns between operating system configuration and home-manager configuration.
- **`OS` Context**: When evaluating for a host's operating system configuration (NixOS or `nix-darwin`), aspects now receive an `OS` attribute in their context. The original `host` object is available as `OS.host`.
- **`HM` Context**: When evaluating for a user's `home-manager` configuration, aspects receive an `HM` attribute. This context contains both the `user` and the `host` they belong to (`HM.user` and `HM.host`).
The previous context objects `userToHost` and `hostToUser` proven to be harder to use and have been **completely removed**. And now contexts are incremental. For example, a NixOS configuration starts with context `{ OS }` but later expands to `{ OS, user, host }` as soon as the OS has information of which users and hosts it is being applied to. See the examples on aspects.nix for how this replaces `userToHost` and `hostToUser`.
### Migration Guide
All parametric aspects must be updated to expect the new context structure.
**Before:**
```nix
my-aspect = { userToHost, ... }: {
${userToHost.host.class}.services.foo.enable = true;
};
my-other-aspect = { hostToUser, ... }: {
homeManager.programs.bar.enable = true;
};
```
**After:**
```nix
# OS context is needed only if my-aspect is installed at den.default
my-aspect = { OS, user, host, ... }: {
${host.class}.services.foo.enable = true;
};
# HM intent context is needed only if installed into den.default.
my-other-aspect = { HM, user, host, ... }: {
homeManager.programs.bar.enable = true;
};
```