-
-
Notifications
You must be signed in to change notification settings - Fork 14.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Reverted] Module-builtin assertions, disabling assertions and submodule assertions #97023
Conversation
Awesome stuff! |
Perhaps we could add syntax sugar to make it top-level like It'd be great to have a way to turn all warnings into errors too. I'll have to take a closer look at Which reminds me - |
This is a bit tricky because there's already the
While I don't think this is a good idea, this can already be done with {
options._module.assertions = mkOption {
type = attrsOf (submodule {
type = mkDefault "warning";
});
};
}
I played around with them being collected on a per-module eval basis, such that they could be forwarded from submodules to the main module eval. I might give this another go.
I like calling this |
I think he meant on the same level as |
@Profpatsch The problem is that if a module is just |
+1 for checks |
The shorthand syntax is fundamentally ambiguous, so the same can be argued about We don't have any |
Do we have any modules that are only assertions? In that case we can migrate it manually; it seems low-chance enough that we can do a breaking change here (provided we have a good error message and migration documentation in the NixOS release notes). Is it currently possible to define a module that has only e.g. |
Part of this feature could be a generalized facility for values that propagate to the root of the configuration monoidally, not unlike a writer monad when used in a tree traversal. That's also how assertions and warnings should propagate, so the only extra bits of code you need are the If this resonates with you, great! Otherwise feel free to ignore. |
@roberth I kinda get it, but I think that's a good discussion for another time 😃 Regarding introducing syntactic sugar, I thought long and hard about this now, and am coming to the conclusion that we should not do it. The main insight I had was that In addition, syntactic sugar would interact in non-optimal ways with submodules: You can't do {
myServices.foo.checks.portConflict.enable = lib.mkForce false;
} because due to how submodules are implemented (for the default of In other news, it turns out that I'll also do the renaming from And I think after that this PR should be about ready. |
Also, I'll introduce a |
Initially NixOS#82897 prevented non-visible options from being rendered in the manual, but visible-but-internal options were still being recursed into. This fixes this, aligning the recurse condition here with the one in make-options-doc/default.nix
The effect of this PR has made clear that we can't perform checks locally (entirely within small parts of the configuration), because it introduces strictness. Instead, what we need is to have two global "phases". In the first phase, module evaluation happens exactly as it does now. It must not and can not depend on the checks. In the second phase, the checks are performed. This may still evaluate some parts that weren't evaluated during the first, but as said, won't refer back to the second phase to cause trouble. While we can't enforce this in arbitrary Nix expressions, we can make this easy with the module system. Now it's impossible for modules/submodules to cause the problem, but we haven't actually wired up the checks from the submodules yet. Even without this, the change is already a positive because it standardizes the interface for writing checks and running checks without causing strictness issues. Module authors could wire it up manually with something like
if we expose Alternatively or additionally, we could automate this by scanning all options for submodules.
This won't be a problem with the above proposal, if the nodes are defined through If that's not feasible (I'd be surprised) the code could just use
This can still cause strictness issues that then can't be avoided. Assertions won't be able to use |
This seems to be one of the few 'architectural' weaknesses that can't be addressed by good module design. |
Supermodules (#152785) are another use case for an out-of-band (non- |
I want to look into this again. For now I just rebased this PR on top of latest master to evaluate it again, here's that for reference: infinisil@b013442 |
I think what I'd try is
Making (2) automatic would be nice but also seems like a performance hazard, especially without a minimal module list. It'd be a one-liner inside the
or maybe it should be like this
|
Not having this is a layering violation, as Encountered this again while working on the NixOS test framework #176557. |
A discussion relating to this PR is happening here: #207187 |
while the <literal>"warning"</literal> type will only show a | ||
warning. | ||
''; | ||
type = types.enum [ "error" "warning" ]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"lint"
would be a nice additional type: like warnings, but not shown by default, because they are low priority and may be wrong.
The user would have to enable lint visibility with an option to turn them into trace messages (warnings?), or use a tool like nix repl
to find the lint messages.
... i.e. without adding _any_ new strictness This adds support for warnings in submodules. Fixes NixOS#96006 To recap, NixOS#97023 didn't make it because of unexpected infinite recursions. Context: - Attaching a warning to an expression can create an otherwise unnecessary data dependency, which can lead to infinite mutual recursion when a (typically very necessary) dependency exists in the other direction. - The `warnings` option is largely independent of the evaluation of the configuration, because it only reads from it. Nothing except `system.build.toplevel` reads from `warnings`. Not cyclical, everyone happy. - NixOS#97023 created data dependencies at submodule roots, not just `toplevel` While something like NixOS#97023 is a very general solution that would solve this `mkRenamedOptionModule` with ease, we don't need it for this particular problem. Specifically, in this case, we already have a data dependency where the new option has a definition based on the old option. So, we can attach the warning to the `mkIf` condition of that definition and make it trigger when the old option is about to be read anyway. As a side effect of this change, these particular warnings don't appear in the `warnings` option anymore. Maybe someone used those for testing, but `mkRenamed`* usages aren't really worth testing, so this is a small price to pay for support for renames in submodules.
Motivation for this change
This implements checks (assertions and warnings) supported by the module system directly, instead of just being a NixOS option (see nixos/modules/misc/assertions.nix).
This has the following benefits:
mkRemovedOptionModule
and co. from within submodulesUsage
Checks can now be defined in any module (even submodules) like this:
Triggered assertion will display the attribute name they were defined as in
[..]
:This allows you to easily disable them or convert an assertion to a warning by using the same attribute that was printed:
See the manual changes for more details (such as how submodule assertions work). Here is a screenshot of it:
Fixes #96006
Ping @roberth @nbp @shlevy @edolstra @jD91mZM2 @aanderse
Best reviewed commit-by-commit
Note that in order to be able to change checks (disable or convert to warning), they need to be defined using
_module.checks
, such that they have a name you can reference. Checks defined using the NixOS optionsassertions
orwarnings
can't be changed (even though they're implemented using_module.checks
now). While the intention is that all of the assertions/warnings in NixOS be given a name and be converted to_module.checks
eventually, this PR doesn't do that so it stays manageable in size.Things done