From 2e97caa6d30c3964fc261e02e75bf1e20368b037 Mon Sep 17 00:00:00 2001 From: Silvan Mosberger Date: Thu, 26 Feb 2026 16:49:05 +0100 Subject: [PATCH] stdenv: Use meta.problems for meta.broken underneath And add tests for it --- doc/stdenv/meta.chapter.md | 11 +++++++- doc/using/configuration.chapter.md | 1 + pkgs/stdenv/generic/check-meta.nix | 23 +-------------- pkgs/stdenv/generic/problems.nix | 28 +++++++++++++++---- .../cases/allow-broken-env/default.nix | 15 ++++++++++ .../problems/cases/allow-broken-env/env.nix | 3 ++ .../cases/allow-broken-env/expected-stderr | 1 + .../allow-broken-predicate-match/default.nix | 18 ++++++++++++ .../expected-stderr | 1 + .../default.nix | 18 ++++++++++++ .../expected-stderr | 16 +++++++++++ .../problems/cases/allow-broken/default.nix | 17 +++++++++++ .../cases/allow-broken/expected-stderr | 1 + .../problems/cases/broken-manual/default.nix | 14 ++++++++++ .../cases/broken-manual/expected-stderr | 16 +++++++++++ pkgs/test/problems/cases/broken/default.nix | 14 ++++++++++ .../problems/cases/broken/expected-stderr | 16 +++++++++++ .../cases/invalid-kind-error/expected-stderr | 2 +- pkgs/test/problems/default.nix | 12 +++++++- pkgs/top-level/config.nix | 4 +++ 20 files changed, 201 insertions(+), 30 deletions(-) create mode 100644 pkgs/test/problems/cases/allow-broken-env/default.nix create mode 100644 pkgs/test/problems/cases/allow-broken-env/env.nix create mode 100644 pkgs/test/problems/cases/allow-broken-env/expected-stderr create mode 100644 pkgs/test/problems/cases/allow-broken-predicate-match/default.nix create mode 100644 pkgs/test/problems/cases/allow-broken-predicate-match/expected-stderr create mode 100644 pkgs/test/problems/cases/allow-broken-predicate-mismatch/default.nix create mode 100644 pkgs/test/problems/cases/allow-broken-predicate-mismatch/expected-stderr create mode 100644 pkgs/test/problems/cases/allow-broken/default.nix create mode 100644 pkgs/test/problems/cases/allow-broken/expected-stderr create mode 100644 pkgs/test/problems/cases/broken-manual/default.nix create mode 100644 pkgs/test/problems/cases/broken-manual/expected-stderr create mode 100644 pkgs/test/problems/cases/broken/default.nix create mode 100644 pkgs/test/problems/cases/broken/expected-stderr diff --git a/doc/stdenv/meta.chapter.md b/doc/stdenv/meta.chapter.md index 947009869ff1..fc53dbe37baf 100644 --- a/doc/stdenv/meta.chapter.md +++ b/doc/stdenv/meta.chapter.md @@ -153,7 +153,7 @@ The list of Nix platform types for which the [Hydra](https://github.com/nixos/hy ### `broken` {#var-meta-broken} -If set to `true`, the package is marked as "broken", meaning that it won’t show up in [search.nixos.org](https://search.nixos.org/packages), and cannot be built or installed unless the environment variable [`NIXPKGS_ALLOW_BROKEN`](#opt-allowBroken) is set. +If set to `true`, the package is marked as "broken", meaning that it won’t show up in [search.nixos.org](https://search.nixos.org/packages), and cannot be built or installed unless [explicitly allowed](#sec-allow-broken). Such unconditionally-broken packages should be removed from Nixpkgs eventually unless they are fixed. The value of this attribute can depend on a package's arguments, including `stdenv`. @@ -181,6 +181,15 @@ This means that `broken` can be used to express constraints, for example: This makes `broken` strictly more powerful than `meta.badPlatforms`. However `meta.availableOn` currently examines only `meta.platforms` and `meta.badPlatforms`, so `meta.broken` does not influence the default values for optional dependencies. +Underneath, `meta.broken = true;` is the same as +```nix +{ + meta.problems.broken.message = "This package is broken."; +} +``` + +By specifying this manually, the error message can be customised. + ## `knownVulnerabilities` {#var-meta-knownVulnerabilities} A list of known vulnerabilities affecting the package, usually identified by CVE identifiers. diff --git a/doc/using/configuration.chapter.md b/doc/using/configuration.chapter.md index e47e35fc1aa4..9c23253549f7 100644 --- a/doc/using/configuration.chapter.md +++ b/doc/using/configuration.chapter.md @@ -179,6 +179,7 @@ Currently, the following problem kinds are known (with more reserved to be added - "removal": The package is planned to be removed some time in the future. Unique. - "deprecated": The package relies on software which has reached its end of life. - "maintainerless": Automatically generated for packages with `meta.maintainers == []`. Unique, not manually specifiable. +- "broken": Automatically generated for packages with `meta.broken = true`. Each problem has a handler that deals with it, which can be one of "error", "warn" or "ignore". "error" will disallow evaluating a package, while "warn" will simply print a message to the log. diff --git a/pkgs/stdenv/generic/check-meta.nix b/pkgs/stdenv/generic/check-meta.nix index cb5b6b7b50c7..7c696fdb53b3 100644 --- a/pkgs/stdenv/generic/check-meta.nix +++ b/pkgs/stdenv/generic/check-meta.nix @@ -99,8 +99,6 @@ let hasBlocklistedLicense = hasListedLicense blocklist; - allowBroken = config.allowBroken || getEnv "NIXPKGS_ALLOW_BROKEN" == "1"; - allowUnsupportedSystem = config.allowUnsupportedSystem || getEnv "NIXPKGS_ALLOW_UNSUPPORTED_SYSTEM" == "1"; @@ -121,18 +119,6 @@ let isMarkedBroken = attrs: attrs.meta.broken or false; - # Allow granular checks to allow only some broken packages - # Example: - # { pkgs, ... }: - # { - # allowBroken = false; - # allowBrokenPredicate = pkg: builtins.elem (pkgs.lib.getName pkg) [ "hello" ]; - # } - allowBrokenPredicate = config.allowBrokenPredicate or (x: false); - - hasDeniedBroken = - attrs: (attrs.meta.broken or false) && !allowBroken && !allowBrokenPredicate attrs; - hasUnsupportedPlatform = pkg: !(availableOn hostPlatform pkg); isMarkedInsecure = attrs: (attrs.meta.knownVulnerabilities or [ ]) != [ ]; @@ -203,7 +189,6 @@ let allow_attr: { Unfree = "NIXPKGS_ALLOW_UNFREE"; - Broken = "NIXPKGS_ALLOW_BROKEN"; UnsupportedSystem = "NIXPKGS_ALLOW_UNSUPPORTED_SYSTEM"; NonSource = "NIXPKGS_ALLOW_NONSOURCE"; } @@ -212,7 +197,6 @@ let allow_attr: { Unfree = "unfree packages"; - Broken = "broken packages"; UnsupportedSystem = "packages that are unsupported for this system"; NonSource = "packages not built from source"; } @@ -357,6 +341,7 @@ let pkgConfigModules = listOf str; inherit platforms; hydraPlatforms = listOf str; + # Automatically turns into meta.problems.broken, see ./problems.nix broken = bool; unfree = bool; unsupported = bool; @@ -453,12 +438,6 @@ let msg = "contains elements not built from source (‘${showSourceType attrs.meta.sourceProvenance}’)"; remediation = remediate_allowlist "NonSource" (remediate_predicate "allowNonSourcePredicate" attrs); } - else if hasDeniedBroken attrs then - { - reason = "broken"; - msg = "is marked as broken"; - remediation = remediate_allowlist "Broken" ""; - } else if hasUnsupportedPlatform attrs && !allowUnsupportedSystem then let toPretty' = toPretty { diff --git a/pkgs/stdenv/generic/problems.nix b/pkgs/stdenv/generic/problems.nix index 04fd0b09dba2..46d2797e248f 100644 --- a/pkgs/stdenv/generic/problems.nix +++ b/pkgs/stdenv/generic/problems.nix @@ -78,6 +78,7 @@ rec { manual = [ "removal" "deprecated" + "broken" ]; # Problem kinds that are currently only allowed to be specified once unique = [ @@ -101,7 +102,7 @@ rec { # 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. - attrs: + config: attrs: # Order of checks optimised for short-circuiting the common case of having maintainers (attrs.meta.maintainers or [ ] == [ ]) && (attrs.meta.teams or [ ] == [ ]) @@ -109,13 +110,26 @@ rec { && (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"; + + allowBrokenPredicate = config.allowBrokenPredicate or (x: false); + in + attrs: attrs.meta.broken or false && !allowBroken && !allowBrokenPredicate attrs; + value.message = "This package is broken."; + } ]; genAutomaticProblems = - attrs: + config: attrs: listToAttrs ( map (problem: lib.nameValuePair problem.kindName problem.value) ( - filter (problem: problem.condition attrs) automaticProblems + filter (problem: problem.condition config attrs) automaticProblems ) ); @@ -419,6 +433,10 @@ rec { inherit (genHandlerSwitch config) handlerForProblem ; + # Makes sure that automatic problems can cache with just config applied + automaticProblemsConfigCache = map ( + problem: problem // { condition = problem.condition config; } + ) automaticProblems; in attrs: let @@ -431,7 +449,7 @@ rec { all ( problem: problem.condition attrs -> handlerForProblem pname problem.kindName problem.kindName == "ignore" - ) automaticProblems + ) automaticProblemsConfigCache && ( # No manual problems manualProblems == { } @@ -445,7 +463,7 @@ rec { else # Slow path, only here we actually figure out which problems we need to handle let - problems = attrs.meta.problems or { } // genAutomaticProblems attrs; + problems = attrs.meta.problems or { } // genAutomaticProblems config attrs; problemsToHandle = filter (v: v.handler != "ignore") ( mapAttrsToList (name: problem: rec { inherit name; diff --git a/pkgs/test/problems/cases/allow-broken-env/default.nix b/pkgs/test/problems/cases/allow-broken-env/default.nix new file mode 100644 index 000000000000..bb24b570d5b5 --- /dev/null +++ b/pkgs/test/problems/cases/allow-broken-env/default.nix @@ -0,0 +1,15 @@ +{ nixpkgs }: +let + pkgs = import nixpkgs { + system = "x86_64-linux"; + overlays = [ ]; + config = { }; + }; +in +pkgs.stdenvNoCC.mkDerivation { + pname = "a"; + version = "0"; + meta.maintainers = [ "hello" ]; + meta.description = "Some package"; + meta.broken = true; +} diff --git a/pkgs/test/problems/cases/allow-broken-env/env.nix b/pkgs/test/problems/cases/allow-broken-env/env.nix new file mode 100644 index 000000000000..6b092e9ef28b --- /dev/null +++ b/pkgs/test/problems/cases/allow-broken-env/env.nix @@ -0,0 +1,3 @@ +{ + NIXPKGS_ALLOW_BROKEN = 1; +} diff --git a/pkgs/test/problems/cases/allow-broken-env/expected-stderr b/pkgs/test/problems/cases/allow-broken-env/expected-stderr new file mode 100644 index 000000000000..c255c1d5a32b --- /dev/null +++ b/pkgs/test/problems/cases/allow-broken-env/expected-stderr @@ -0,0 +1 @@ +warning: you did not specify '--add-root'; the result might be removed by the garbage collector diff --git a/pkgs/test/problems/cases/allow-broken-predicate-match/default.nix b/pkgs/test/problems/cases/allow-broken-predicate-match/default.nix new file mode 100644 index 000000000000..7869bc996809 --- /dev/null +++ b/pkgs/test/problems/cases/allow-broken-predicate-match/default.nix @@ -0,0 +1,18 @@ +{ nixpkgs }: +let + lib = pkgs.lib; + pkgs = import nixpkgs { + system = "x86_64-linux"; + overlays = [ ]; + config = { + allowBrokenPredicate = attrs: lib.getName attrs == "a"; + }; + }; +in +pkgs.stdenvNoCC.mkDerivation { + pname = "a"; + version = "0"; + meta.maintainers = [ "hello" ]; + meta.description = "Some package"; + meta.broken = true; +} diff --git a/pkgs/test/problems/cases/allow-broken-predicate-match/expected-stderr b/pkgs/test/problems/cases/allow-broken-predicate-match/expected-stderr new file mode 100644 index 000000000000..c255c1d5a32b --- /dev/null +++ b/pkgs/test/problems/cases/allow-broken-predicate-match/expected-stderr @@ -0,0 +1 @@ +warning: you did not specify '--add-root'; the result might be removed by the garbage collector diff --git a/pkgs/test/problems/cases/allow-broken-predicate-mismatch/default.nix b/pkgs/test/problems/cases/allow-broken-predicate-mismatch/default.nix new file mode 100644 index 000000000000..572fd463a9c5 --- /dev/null +++ b/pkgs/test/problems/cases/allow-broken-predicate-mismatch/default.nix @@ -0,0 +1,18 @@ +{ nixpkgs }: +let + lib = pkgs.lib; + pkgs = import nixpkgs { + system = "x86_64-linux"; + overlays = [ ]; + config = { + allowBrokenPredicate = attrs: lib.getName attrs == "b"; + }; + }; +in +pkgs.stdenvNoCC.mkDerivation { + pname = "a"; + version = "0"; + meta.maintainers = [ "hello" ]; + meta.description = "Some package"; + meta.broken = true; +} diff --git a/pkgs/test/problems/cases/allow-broken-predicate-mismatch/expected-stderr b/pkgs/test/problems/cases/allow-broken-predicate-mismatch/expected-stderr new file mode 100644 index 000000000000..c57089a678f6 --- /dev/null +++ b/pkgs/test/problems/cases/allow-broken-predicate-mismatch/expected-stderr @@ -0,0 +1,16 @@ +(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:16 because it has problems: +- broken: This package is broken. + +See also https://nixos.org/manual/nixpkgs/unstable#sec-problems +To allow evaluation regardless, use: +- Nixpkgs import: import nixpkgs { config = ; } +- NixOS: nixpkgs.config = ; +- nix-* commands: Put below code in ~/.config/nixpkgs/config.nix + + { + problems.handlers = { + a.broken = "warn"; # or "ignore" + }; + } diff --git a/pkgs/test/problems/cases/allow-broken/default.nix b/pkgs/test/problems/cases/allow-broken/default.nix new file mode 100644 index 000000000000..8c3aa2624495 --- /dev/null +++ b/pkgs/test/problems/cases/allow-broken/default.nix @@ -0,0 +1,17 @@ +{ nixpkgs }: +let + pkgs = import nixpkgs { + system = "x86_64-linux"; + overlays = [ ]; + config = { + allowBroken = true; + }; + }; +in +pkgs.stdenvNoCC.mkDerivation { + pname = "a"; + version = "0"; + meta.maintainers = [ "hello" ]; + meta.description = "Some package"; + meta.broken = true; +} diff --git a/pkgs/test/problems/cases/allow-broken/expected-stderr b/pkgs/test/problems/cases/allow-broken/expected-stderr new file mode 100644 index 000000000000..c255c1d5a32b --- /dev/null +++ b/pkgs/test/problems/cases/allow-broken/expected-stderr @@ -0,0 +1 @@ +warning: you did not specify '--add-root'; the result might be removed by the garbage collector diff --git a/pkgs/test/problems/cases/broken-manual/default.nix b/pkgs/test/problems/cases/broken-manual/default.nix new file mode 100644 index 000000000000..3e32f03c584e --- /dev/null +++ b/pkgs/test/problems/cases/broken-manual/default.nix @@ -0,0 +1,14 @@ +{ nixpkgs }: +let + pkgs = import nixpkgs { + system = "x86_64-linux"; + overlays = [ ]; + config = { }; + }; +in +pkgs.stdenvNoCC.mkDerivation { + pname = "a"; + version = "0"; + meta.description = "Some package"; + meta.problems.broken.message = "This package is broken because horse."; +} diff --git a/pkgs/test/problems/cases/broken-manual/expected-stderr b/pkgs/test/problems/cases/broken-manual/expected-stderr new file mode 100644 index 000000000000..d16295c414f5 --- /dev/null +++ b/pkgs/test/problems/cases/broken-manual/expected-stderr @@ -0,0 +1,16 @@ +(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:12 because it has problems: +- broken: This package is broken because horse. + +See also https://nixos.org/manual/nixpkgs/unstable#sec-problems +To allow evaluation regardless, use: +- Nixpkgs import: import nixpkgs { config = ; } +- NixOS: nixpkgs.config = ; +- nix-* commands: Put below code in ~/.config/nixpkgs/config.nix + + { + problems.handlers = { + a.broken = "warn"; # or "ignore" + }; + } diff --git a/pkgs/test/problems/cases/broken/default.nix b/pkgs/test/problems/cases/broken/default.nix new file mode 100644 index 000000000000..87b9a7eb67fe --- /dev/null +++ b/pkgs/test/problems/cases/broken/default.nix @@ -0,0 +1,14 @@ +{ nixpkgs }: +let + pkgs = import nixpkgs { + system = "x86_64-linux"; + overlays = [ ]; + config = { }; + }; +in +pkgs.stdenvNoCC.mkDerivation { + pname = "a"; + version = "0"; + meta.description = "Some package"; + meta.broken = true; +} diff --git a/pkgs/test/problems/cases/broken/expected-stderr b/pkgs/test/problems/cases/broken/expected-stderr new file mode 100644 index 000000000000..010f4776b2f5 --- /dev/null +++ b/pkgs/test/problems/cases/broken/expected-stderr @@ -0,0 +1,16 @@ +(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:12 because it has problems: +- broken: This package is broken. + +See also https://nixos.org/manual/nixpkgs/unstable#sec-problems +To allow evaluation regardless, use: +- Nixpkgs import: import nixpkgs { config = ; } +- NixOS: nixpkgs.config = ; +- nix-* commands: Put below code in ~/.config/nixpkgs/config.nix + + { + problems.handlers = { + a.broken = "warn"; # or "ignore" + }; + } diff --git a/pkgs/test/problems/cases/invalid-kind-error/expected-stderr b/pkgs/test/problems/cases/invalid-kind-error/expected-stderr index dbe13602c3f8..07c5a38e1bcb 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` diff --git a/pkgs/test/problems/default.nix b/pkgs/test/problems/default.nix index 1e2ce5108bbe..e65606362376 100644 --- a/pkgs/test/problems/default.nix +++ b/pkgs/test/problems/default.nix @@ -20,7 +20,17 @@ lib.mapAttrs ( export NIX_STATE_DIR=$(mktemp -d) mkdir $out - command=( + + command=() + ${lib.optionalString (builtins.pathExists (./cases + "/${name}/env.nix")) '' + command+=( + env + ${lib.concatMapAttrsStringSep "\n" (name: value: "${name}=${toString value}") ( + import (./cases + "/${name}/env.nix") + )} + ) + ''} + command+=( # FIXME: Using this version because it doesn't print a trace by default # Probably should have some regex-style error matching instead "${lib.getBin nix}/bin/nix-instantiate" diff --git a/pkgs/top-level/config.nix b/pkgs/top-level/config.nix index 8f700a07eb0e..b7f55f143576 100644 --- a/pkgs/top-level/config.nix +++ b/pkgs/top-level/config.nix @@ -498,6 +498,10 @@ in # Put the default value for matchers in here (as in, not as an *actual* mkDefault default value), # to force it being merged with any custom values instead of being overridden. problems.matchers = [ + { + kind = "broken"; + handler = "error"; + } # Be loud and clear about package removals { kind = "removal";