From 93ea59f66d5e33c0734f2c8f27d7b3c0b4207ab3 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 23 Oct 2025 18:31:35 +0200 Subject: [PATCH] lib/modules: Report error for unsupported // { check } `check` can have a new place since the introduction of merge.v2. This makes the // { check = ... } idiom unreliable. In this PR we add checks to detect and report this. merge.v2 introduced in: https://github.com/NixOS/nixpkgs/pull/391544 Real world case: https://hercules-ci.com/github/hercules-ci/hercules-ci-effects/jobs/875 --- lib/modules.nix | 34 ++++++- lib/tests/modules.sh | 19 ++++ lib/tests/modules/v2-check-coherence.nix | 117 +++++++++++++++++++++++ lib/types.nix | 83 ++++++++++++---- 4 files changed, 232 insertions(+), 21 deletions(-) create mode 100644 lib/tests/modules/v2-check-coherence.nix diff --git a/lib/modules.nix b/lib/modules.nix index 42e88dbbf625..09f0908a69da 100644 --- a/lib/modules.nix +++ b/lib/modules.nix @@ -1126,6 +1126,29 @@ let __toString = _: showOption loc; }; + # Check that a type with v2 merge has a coherent check attribute. + # Throws an error if the type uses an ad-hoc `type // { check }` override. + # Returns the last argument like `seq`, allowing usage: checkV2MergeCoherence loc type expr + checkV2MergeCoherence = + loc: type: result: + if type.check.isV2MergeCoherent or false then + result + else + throw '' + The option `${showOption loc}' has a type `${type.description}' that uses + an ad-hoc `type // { check = ...; }' override, which is incompatible with + the v2 merge mechanism. + + Please use `lib.types.addCheck` instead of `type // { check }' to add + custom validation. For example: + + lib.types.addCheck baseType (value: /* your check */) + + instead of: + + baseType // { check = value: /* your check */; } + ''; + # Merge definitions of a value of a given type. mergeDefinitions = loc: type: defs: rec { defsFinal' = @@ -1201,10 +1224,13 @@ let ( if type.merge ? v2 then let - r = type.merge.v2 { - inherit loc; - defs = defsFinal; - }; + # Check for v2 merge coherence + r = checkV2MergeCoherence loc type ( + type.merge.v2 { + inherit loc; + defs = defsFinal; + } + ); in r // { diff --git a/lib/tests/modules.sh b/lib/tests/modules.sh index 5cd781752317..02f73310dd9b 100755 --- a/lib/tests/modules.sh +++ b/lib/tests/modules.sh @@ -806,6 +806,25 @@ checkConfigError 'A definition for option .* is not of type .signed integer.*' c checkConfigOutput '^true$' config.v2checkedPass ./add-check.nix checkConfigError 'A definition for option .* is not of type .attribute set of signed integer.*' config.v2checkedFail ./add-check.nix +# v2 merge check coherence +# Tests checkV2MergeCoherence call in modules.nix (mergeDefinitions for lazyAttrsOf) +checkConfigError 'ad-hoc.*override.*incompatible' config.adhocFail.foo ./v2-check-coherence.nix +# Tests checkV2MergeCoherence call in modules.nix (mergeDefinitions for lazyAttrsOf) +checkConfigError 'ad-hoc.*override.*incompatible' config.adhocOuterFail.bar ./v2-check-coherence.nix +# Tests checkV2MergeCoherence call in types.nix (either t1) +checkConfigError 'ad-hoc.*override.*incompatible' config.eitherLeftFail ./v2-check-coherence.nix +# Tests checkV2MergeCoherence call in types.nix (either t2) +checkConfigError 'ad-hoc.*override.*incompatible' config.eitherRightFail ./v2-check-coherence.nix +# Tests checkV2MergeCoherence call in types.nix (coercedTo coercedType) +checkConfigError 'ad-hoc.*override.*incompatible' config.coercedFromFail.bar ./v2-check-coherence.nix +# Tests checkV2MergeCoherence call in types.nix (coercedTo finalType) +checkConfigError 'ad-hoc.*override.*incompatible' config.coercedToFail.foo ./v2-check-coherence.nix +# Tests checkV2MergeCoherence call in types.nix (addCheck elemType) +checkConfigError 'ad-hoc.*override.*incompatible' config.addCheckNested.foo ./v2-check-coherence.nix +checkConfigError 'Please use.*lib.types.addCheck.*instead' config.adhocFail.foo ./v2-check-coherence.nix +checkConfigError 'A definition for option .* is not of type .*' config.addCheckFail.bar.baz ./v2-check-coherence.nix +checkConfigOutput '^true$' config.result ./v2-check-coherence.nix + cat <