From ff79595a294f25ccb7777154a66ce5ef952280a0 Mon Sep 17 00:00:00 2001 From: Silvan Mosberger Date: Fri, 19 Jun 2026 15:10:05 +0200 Subject: [PATCH] meta.problems: Internal refactoring Makes future changes easier (cherry picked from commit eba1d513522ab485ec21874498f72e8b011b576a) --- pkgs/stdenv/generic/problems.nix | 131 +++++++++--------- .../cases/invalid-kind-error/expected-stderr | 2 +- 2 files changed, 69 insertions(+), 64 deletions(-) diff --git a/pkgs/stdenv/generic/problems.nix b/pkgs/stdenv/generic/problems.nix index 0f03c393e384..1451e6161c90 100644 --- a/pkgs/stdenv/generic/problems.nix +++ b/pkgs/stdenv/generic/problems.nix @@ -72,68 +72,73 @@ rec { max = a: b: if lessThan a b then b else a; }; - # TODO: Combine this and automaticProblems into a `{ removal = { manual = true; ... }; ... }` structure for less error-prone changes - kinds = rec { - # Automatic and manual problem kinds - known = map (problem: problem.kindName) automaticProblems ++ manual; - # Problem kinds that are currently allowed to be specified in `meta.problems` - manual = [ - "removal" - "deprecated" - "broken" - ]; - # Problem kinds that are currently only allowed to be specified once - unique = [ - "removal" - ]; + kinds = { + maintainerless = { + manualAllowed = false; + isUnique = false; + automatic = { + condition = + # To get usable output, we want to avoid flagging "internal" derivations. + # Because we do not have a way to reliably decide between internal or + # external derivation, some heuristics are required to decide. + # + # If `outputHash` is defined, the derivation is a FOD, such as the output of a fetcher. + # If `description` is not defined, the derivation is probably not a package. + # Simply checking whether `meta` is defined is insufficient, + # as some fetchers and trivial builders do define meta. + config: attrs: + # Order of checks optimised for short-circuiting the common case of having maintainers + (attrs.meta.maintainers or [ ] == [ ]) + && (attrs.meta.teams or [ ] == [ ]) + && (!attrs ? outputHash) + && (attrs ? meta.description); + value.message = "This package has no declared maintainer, i.e. an empty `meta.maintainers` and `meta.teams` attribute."; + }; + }; + broken = { + manualAllowed = true; + isUnique = false; + automatic = { + condition = + config: + let + # TODO: Consider deprecating this or making it generic for all problems + allowBroken = config.allowBroken || builtins.getEnv "NIXPKGS_ALLOW_BROKEN" == "1"; - # Same thing but a set with null values (comes in handy at times) - manual' = genAttrs manual (k: null); - unique' = genAttrs unique (k: null); + allowBrokenPredicate = + lib.warnIf (lib.oldestSupportedReleaseIsAtLeast 2605) + "config.allowBrokenPredicate is deprecated, use config.problems.handlers.myPackage.broken = \"warn\" for individual packages instead." + config.allowBrokenPredicate; + in + if allowBroken then + attrs: false + else if config ? allowBrokenPredicate then + attrs: attrs ? meta.broken && attrs.meta.broken && !allowBrokenPredicate attrs + else + attrs: attrs ? meta.broken && attrs.meta.broken; + value.message = "This package is broken."; + }; + }; + removal = { + manualAllowed = true; + isUnique = true; + automatic = null; + }; + deprecated = { + manualAllowed = true; + isUnique = false; + automatic = null; + }; }; - automaticProblems = [ - { - kindName = "maintainerless"; - condition = - # To get usable output, we want to avoid flagging "internal" derivations. - # Because we do not have a way to reliably decide between internal or - # external derivation, some heuristics are required to decide. - # - # If `outputHash` is defined, the derivation is a FOD, such as the output of a fetcher. - # If `description` is not defined, the derivation is probably not a package. - # Simply checking whether `meta` is defined is insufficient, - # as some fetchers and trivial builders do define meta. - config: attrs: - # Order of checks optimised for short-circuiting the common case of having maintainers - (attrs.meta.maintainers or [ ] == [ ]) - && (attrs.meta.teams or [ ] == [ ]) - && (!attrs ? outputHash) - && (attrs ? meta.description); - value.message = "This package has no declared maintainer, i.e. an empty `meta.maintainers` and `meta.teams` attribute."; - } - { - kindName = "broken"; - condition = - config: - let - # TODO: Consider deprecating this or making it generic for all problems - allowBroken = config.allowBroken || builtins.getEnv "NIXPKGS_ALLOW_BROKEN" == "1"; + # Problem kinds that are currently allowed to be specified in `meta.problems` + manualKinds = lib.filterAttrs (name: value: value.manualAllowed) kinds; + # Problem kinds that are currently only allowed to be specified once + uniqueKinds = lib.filterAttrs (name: value: value.isUnique) kinds; - allowBrokenPredicate = - lib.warnIf (lib.oldestSupportedReleaseIsAtLeast 2605) - "config.allowBrokenPredicate is deprecated, use config.problems.handlers.myPackage.broken = \"warn\" for individual packages instead." - config.allowBrokenPredicate; - in - if allowBroken then - attrs: false - else if config ? allowBrokenPredicate then - attrs: attrs ? meta.broken && attrs.meta.broken && !allowBrokenPredicate attrs - else - attrs: attrs ? meta.broken && attrs.meta.broken; - value.message = "This package is broken."; - } - ]; + automaticProblems = lib.mapAttrsToList (name: value: value.automatic // { kindName = name; }) ( + lib.filterAttrs (name: value: value.automatic != null) kinds + ); genAutomaticProblems = config: attrs: @@ -148,7 +153,7 @@ rec { let types = lib.types; handlerType = types.enum handlers.levels; - problemKindType = types.enum kinds.known; + problemKindType = types.enum (attrNames kinds); in { handlers = lib.mkOption { @@ -250,7 +255,7 @@ rec { record enum ; - kindType = enum kinds.manual; + kindType = enum (attrNames manualKinds); subRecord = record { kind = kindType; message = str; @@ -270,7 +275,7 @@ rec { let kindGroups = groupBy (kind: kind) (mapAttrsToList (name: problem: problem.kind or name) v); in - all (kind: kinds.manual' ? ${kind} && (kinds.unique' ? ${kind} -> length kindGroups.${kind} == 1)) ( + all (kind: manualKinds ? ${kind} && (uniqueKinds ? ${kind} -> length kindGroups.${kind} == 1)) ( attrNames kindGroups ) ); @@ -294,14 +299,14 @@ rec { ++ concatLists ( mapAttrsToList ( kind: kindGroup: - optionals (!kinds.manual' ? ${kind}) ( + optionals (!manualKinds ? ${kind}) ( map ( el: "${ctx}.${el.name}: Problem kind ${kind}, inferred from the problem name, is invalid; expected ${kindType.name}. You can specify an explicit problem kind with `${ctx}.${el.name}.kind`" ) (filter (el: !el.explicit) kindGroup) ) ++ - optional (kinds.unique' ? ${kind} && length kindGroup > 1) + optional (uniqueKinds ? ${kind} && length kindGroup > 1) "${ctx}: Problem kind ${kind} should be unique, but is used for these problems: ${ concatMapStringsSep ", " (el: el.name) kindGroup }" diff --git a/pkgs/test/problems/cases/invalid-kind-error/expected-stderr b/pkgs/test/problems/cases/invalid-kind-error/expected-stderr index 07c5a38e1bcb..6fdf2b1d04b1 100644 --- a/pkgs/test/problems/cases/invalid-kind-error/expected-stderr +++ b/pkgs/test/problems/cases/invalid-kind-error/expected-stderr @@ -1,4 +1,4 @@ (stack trace truncated; use '--show-trace' to show the full, detailed trace) error: Refusing to evaluate package 'a-0' in /nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-default.nix:11 because it has an invalid meta attrset: - - a.meta.problems.invalid: Problem kind invalid, inferred from the problem name, is invalid; expected enum. You can specify an explicit problem kind with `a.meta.problems.invalid.kind` + - a.meta.problems.invalid: Problem kind invalid, inferred from the problem name, is invalid; expected enum. You can specify an explicit problem kind with `a.meta.problems.invalid.kind`