diff --git a/ci/OWNERS b/ci/OWNERS index 8f741686635f..695c91aa1af0 100644 --- a/ci/OWNERS +++ b/ci/OWNERS @@ -58,8 +58,10 @@ /pkgs/top-level/by-name-overlay.nix @infinisil @philiptaron /pkgs/stdenv @philiptaron @NixOS/stdenv /pkgs/stdenv/generic @Ericson2314 @NixOS/stdenv -/pkgs/stdenv/generic/check-meta.nix @Ericson2314 @adisbladis @NixOS/stdenv -/pkgs/stdenv/generic/meta-types.nix @adisbladis @NixOS/stdenv +/pkgs/stdenv/generic/problems.nix @infinisil +/pkgs/test/problems @infinisil +/pkgs/stdenv/generic/check-meta.nix @infinisil @Ericson2314 @adisbladis @NixOS/stdenv +/pkgs/stdenv/generic/meta-types.nix @infinisil @adisbladis @NixOS/stdenv /pkgs/stdenv/cross @Ericson2314 @NixOS/stdenv /pkgs/build-support @philiptaron /pkgs/build-support/cc-wrapper @Ericson2314 diff --git a/doc/redirects.json b/doc/redirects.json index ad19777f2dd2..f5dbb8bb65f8 100644 --- a/doc/redirects.json +++ b/doc/redirects.json @@ -629,6 +629,9 @@ "chap-stdenv": [ "index.html#chap-stdenv" ], + "sec-problems": [ + "index.html#sec-problems" + ], "sec-using-llvm": [ "index.html#sec-using-llvm" ], diff --git a/doc/using/configuration.chapter.md b/doc/using/configuration.chapter.md index ac67d7a61459..e47e35fc1aa4 100644 --- a/doc/using/configuration.chapter.md +++ b/doc/using/configuration.chapter.md @@ -11,6 +11,8 @@ By default, Nix will prevent installation if any of the following criteria are t - The package has known security vulnerabilities but has not or can not be updated for some reason, and a list of issues has been entered into the package's `meta.knownVulnerabilities`. +- There are problems for packages which must be acknowledged, e.g. deprecation notices. + Each of these criteria can be altered in the Nixpkgs configuration. :::{.note} @@ -166,6 +168,57 @@ There are several ways to tweak how Nix handles a package which has been marked Note that `permittedInsecurePackages` is only checked if `allowInsecurePredicate` is not specified. +## Packages with problems {#sec-problems} + +A package may have several problems associated with it. +These can be either manually declared in `meta.problems`, or automatically generated from its other `meta` attributes. +Each problem has a name, a "kind", a message, and optionally a list of URLs. +Not all kinds can be manually specified in `meta.problems`, and some kinds can exist only up to once per package. +Currently, the following problem kinds are known (with more reserved to be added in the future): + +- "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. + +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. + +The handler for problems can be specified using `config.problems.handlers.${packageName}.${problemName} = "${handler}";`. + +There is also the possibility to specify some generic matchers, which can set a handler for more than a specific problem of a specific package. +This works through the `config.problems.matchers` option: + +```nix +{ + problems.matchers = [ + # Fail to build any packages which are about to be removed anyway + { + kind = "removal"; + handler = "error"; + } + + # Get warnings when using packages with no declared maintainers + { + kind = "maintainerless"; + handler = "warn"; + } + + # You deeply care about this package and want to absolutely know when it has any problems + { + package = "hello"; + handler = "error"; + } + ]; +} +``` + +Matchers can match one or more of package name, problem name or problem kind. +If multiple conditions are present, all must be met to match. +If multiple matchers match a problem, then the highest severity handler will be chosen. +The current default value contains `{ kind = "removal"; handler = "warn"; }`, meaning that people will be notified about package removals in advance. + +Package names for both `problems.handlers` and `problems.matchers` are taken from `lib.getName`, which looks at the `pname` first and falls back to extracting the "pname" part from the `name` attribute. + ## Modify packages via `packageOverrides` {#sec-modify-via-packageOverrides} You can define a function called `packageOverrides` in your local `~/.config/nixpkgs/config.nix` to override Nix packages. It must be a function that takes pkgs as an argument and returns a modified set of packages. diff --git a/pkgs/stdenv/generic/check-meta.nix b/pkgs/stdenv/generic/check-meta.nix index 014d8a088781..27f8c854ec7e 100644 --- a/pkgs/stdenv/generic/check-meta.nix +++ b/pkgs/stdenv/generic/check-meta.nix @@ -10,10 +10,8 @@ let inherit (lib) all - attrNames attrValues concatMapStrings - concatMapStringsSep concatStrings filter findFirst @@ -26,7 +24,8 @@ let optionalString isAttrs isString - mapAttrs + warn + foldl' ; inherit (lib.lists) @@ -49,15 +48,17 @@ let inherit (builtins) getEnv - trace ; + inherit (import ./problems.nix { inherit lib; }) + problemsType + genCheckProblems + ; + checkProblems = genCheckProblems config; + # If we're in hydra, we can dispense with the more verbose error # messages and make problems easier to spot. inHydra = config.inHydra or false; - # Allow the user to opt-into additional warnings, e.g. - # import { config = { showDerivationWarnings = [ "maintainerless" ]; }; } - showWarnings = config.showDerivationWarnings; getNameWithVersion = attrs: attrs.name or "${attrs.pname or "«name-missing»"}-${attrs.version or "«version-missing»"}"; @@ -118,21 +119,6 @@ let hasUnfreeLicense = attrs: attrs ? meta.license && isUnfree attrs.meta.license; - hasNoMaintainers = - # 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. - attrs: - (!attrs ? outputHash) - && (attrs ? meta.description) - && (attrs.meta.maintainers or [ ] == [ ]) - && (attrs.meta.teams or [ ] == [ ]); - isMarkedBroken = attrs: attrs.meta.broken or false; # Allow granular checks to allow only some broken packages @@ -375,6 +361,8 @@ let unfree = bool; unsupported = bool; insecure = bool; + # This is checked in more detail further down + problems = problemsType; timeout = int; knownVulnerabilities = listOf str; badPlatforms = platforms; @@ -497,18 +485,6 @@ let else null; - # Please also update the type in /pkgs/top-level/config.nix alongside this. - checkWarnings = - attrs: - if hasNoMaintainers attrs then - { - reason = "maintainerless"; - msg = "has no maintainers or teams"; - remediation = ""; - } - else - null; - # Helper functions and declarations to handle identifiers, extracted to reduce allocations hasAllCPEParts = cpeParts: @@ -669,52 +645,64 @@ let && ((config.checkMetaRecursively or false) -> all (d: d.meta.available or true) references); }; - validYes = { - valid = "yes"; - handled = true; - }; + handle = + { + attrs, + meta, + warnings ? [ ], + error ? null, + }: + let + withError = + if isNull error then + true + else + let + msg = + "Refusing to evaluate package '${getNameWithVersion attrs}' in ${pos_str meta} because it ${error.msg}" + + lib.optionalString (!inHydra && error.remediation != "") "\n${error.remediation}"; + in + if config ? handleEvalIssue then config.handleEvalIssue error.reason msg else throw msg; + + giveWarning = + acc: warning: + let + msg = + "Package '${getNameWithVersion attrs}' in ${pos_str meta} ${warning.msg}" + + lib.optionalString (!inHydra && warning.remediation != "") " ${warning.remediation}"; + in + warn msg acc; + in + # Give all warnings first, then error if any + builtins.seq (foldl' giveWarning null warnings) withError; assertValidity = { meta, attrs }: let invalid = checkValidity attrs; - warning = checkWarnings attrs; + problems = checkProblems attrs; in if isNull invalid then - if isNull warning then - validYes - else - let - msg = - if inHydra then - "Warning while evaluating ${getNameWithVersion attrs}: «${warning.reason}»: ${warning.msg}" - else - "Package ${getNameWithVersion attrs} in ${pos_str meta} ${warning.msg}, continuing anyway." - + (optionalString (warning.remediation != "") "\n${warning.remediation}"); - - handled = if elem warning.reason showWarnings then trace msg true else true; - in + if isNull problems then { - valid = "warn"; - handled = handled; + valid = "yes"; + handled = true; + } + else + { + valid = if isNull problems.error then "warn" else "no"; + handled = handle { + inherit attrs meta; + inherit (problems) error warnings; + }; } else - let - msg = - if inHydra then - "Failed to evaluate ${getNameWithVersion attrs}: «${invalid.reason}»: ${invalid.msg}" - else - '' - Package ‘${getNameWithVersion attrs}’ in ${pos_str meta} ${invalid.msg}, refusing to evaluate. - - '' - + invalid.remediation; - - handled = if config ? handleEvalIssue then config.handleEvalIssue invalid.reason msg else throw msg; - in { valid = "no"; - handled = handled; + handled = handle { + inherit attrs meta; + error = invalid; + }; }; in diff --git a/pkgs/stdenv/generic/meta-types.nix b/pkgs/stdenv/generic/meta-types.nix index 462aef57439e..6e432d1ed609 100644 --- a/pkgs/stdenv/generic/meta-types.nix +++ b/pkgs/stdenv/generic/meta-types.nix @@ -5,7 +5,7 @@ # TODO: add a method to the module system types # see https://github.com/NixOS/nixpkgs/pull/273935#issuecomment-1854173100 let - inherit (builtins) + inherit (lib) isString isInt isAttrs @@ -167,7 +167,7 @@ lib.fix (self: { concatMap ( k: if fieldVerifiers ? ${k} then - lib.optionals (fieldVerifiers.${k} v.${k}) (self.errors fields.${k} (ctx + ".${k}") v.${k}) + lib.optionals (!fieldVerifiers.${k} v.${k}) (self.errors fields.${k} "${ctx}.${k}" v.${k}) else [ "${ctx}: key '${k}' is unrecognized; expected one of: \n [${ diff --git a/pkgs/stdenv/generic/problems.nix b/pkgs/stdenv/generic/problems.nix new file mode 100644 index 000000000000..08d4b6e30ec0 --- /dev/null +++ b/pkgs/stdenv/generic/problems.nix @@ -0,0 +1,515 @@ +/* + This file implements everything around meta.problems, including: + - automaticProblems: Which problems get added automatically based on some condition + - configOptions: Module system options for config.problems + - problemsType: The check for meta.problems + - genHandlerSwitch: The logic to determine the handler for a specific problem based on config.problems + - genCheckProblems: The logic to determine which problems need to be handled and how the messages should look like + + There are tests to cover pretty much this entire file, so please run them when making changes ;) + + nix-build -A tests.problems +*/ + +{ lib }: + +rec { + + inherit (lib.strings) + escapeNixIdentifier + ; + + inherit (lib) + any + listToAttrs + concatStringsSep + optionalString + getName + optionalAttrs + pipe + isString + filterAttrs + mapAttrs + partition + elemAt + max + foldl' + elem + filter + concatMapStringsSep + optional + optionals + concatLists + all + attrNames + attrValues + length + mapAttrsToList + groupBy + subtractLists + genAttrs + ; + + handlers = rec { + # Ordered from less to more + levels = [ + "ignore" + "warn" + "error" + ]; + + lessThan = + a: b: + if a == "error" then + false + else if a == "warn" then + b == "error" + else + b != "ignore"; + + 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" + ]; + # Problem kinds that are currently only allowed to be specified once + unique = [ + "removal" + ]; + + # Same thing but a set with null values (comes in handy at times) + manual' = genAttrs manual (k: null); + unique' = genAttrs unique (k: 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. + 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."; + } + ]; + + genAutomaticProblems = + attrs: + listToAttrs ( + map (problem: lib.nameValuePair problem.kindName problem.value) ( + filter (problem: problem.condition attrs) automaticProblems + ) + ); + + # A module system type for Nixpkgs config + configOptions = + let + types = lib.types; + handlerType = types.enum handlers.levels; + problemKindType = types.enum kinds.known; + in + { + handlers = lib.mkOption { + type = with types; attrsOf (attrsOf handlerType); + default = { }; + description = '' + Specify how to handle packages with problems. + Each key has the format `packageName.problemName`, each value is one of "error", "warn" or "ignore". + + This option takes precedence over anything in `problems.matchers`. + + Package names are taken from `lib.getName`, which looks at the `pname` first and falls back to extracting the "pname" part from the `name` attribute. + + See Installing packages with problems in the NixOS manual. + ''; + }; + + matchers = lib.mkOption { + type = types.listOf ( + types.submodule ( + { config, ... }: + { + options = { + package = lib.mkOption { + type = types.nullOr types.str; + description = "Match problems of packages with this name"; + default = null; + }; + name = lib.mkOption { + type = types.nullOr types.str; + description = "Match problems with this problem name"; + default = null; + }; + kind = lib.mkOption { + type = types.nullOr problemKindType; + description = "Match problems of this problem kind"; + default = null; + }; + handler = lib.mkOption { + type = handlerType; + description = "Specify the handler for matched problems"; + }; + + # Temporary hack to get assertions in submodules, see global assertions below + assertions = lib.mkOption { + type = types.listOf types.anything; + default = [ ]; + internal = true; + }; + }; + config = { + assertions = + # Using optional because otherwise message would be evaluated even when assertion is true + ( + optional (config.package != null && config.name != null) { + assertion = false; + # TODO: Does it really matter if we let people specify this? Maybe not, so consider removing this assertion + message = '' + There is a problems.matchers with `package = "${config.package}"` and `name = "${config.name}". Use the following instead: + problems.handlers.${escapeNixIdentifier config.package}.${escapeNixIdentifier config.name} = "${config.handler}"; + ''; + } + ); + }; + } + ) + ); + default = [ ]; + description = '' + A more powerful and less ergonomic version of `problems.handlers`. + Each value is a matcher, that may match onto certain properties of a problem and specify a handler for them. + + If multiple matchers match a problem, the handler with the highest severity (error > warn > ignore) will be used. + Values in `problems.handlers` always take precedence over matchers. + + Any matchers must not contain both a `package` and `name` field, for this should be handled by using `problems.handlers` instead. + ''; + example = [ + { + kind = "maintainerless"; + handler = "warn"; + } + { + package = "myPackageICareAbout"; + handler = "error"; + } + ]; + }; + }; + + # The type for meta.problems + problemsType = + let + types = import ./meta-types.nix { inherit lib; }; + inherit (types) + str + listOf + attrsOf + record + enum + ; + kindType = enum kinds.manual; + subRecord = record { + kind = kindType; + message = str; + urls = listOf str; + }; + simpleType = attrsOf subRecord; + in + { + name = "problems"; + verify = + v: + v == { } + || + simpleType.verify v + && all (problem: problem ? message) (attrValues v) + && ( + 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)) ( + attrNames kindGroups + ) + ); + errors = + ctx: v: + let + kindGroups = groupBy (attrs: attrs.kind) ( + mapAttrsToList (name: problem: { + inherit name; + explicit = problem ? kind; + kind = problem.kind or name; + }) v + ); + in + if !simpleType.verify v then + types.errors simpleType ctx v + else + concatLists ( + mapAttrsToList (name: p: optional (!p ? message) "${ctx}.${name}: `.message` not specified") v + ) + ++ concatLists ( + mapAttrsToList ( + kind: kindGroup: + optionals (!kinds.manual' ? ${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) + "${ctx}: Problem kind ${kind} should be unique, but is used for these problems: ${ + concatMapStringsSep ", " (el: el.name) kindGroup + }" + ) kindGroups + ); + }; + + /* + Construct a structure as follows, with the invariant that a more specific path always has a stricter handler, forming a lattice. + E.g. if `packageSpecific.foo.nameFallback.kindFallback == "warn"`, then `packageFallback.nameFallback.kindFallback` must be "ignore". + + packageSpecific. = { + nameSpecific. = { + kindSpecific. = ; + kindFallback = ; + }; + nameFallback = { + kindSpecific. = ; + kindFallback = ; + }; + }; + packageFallback = { + nameSpecific. = { + kindSpecific. = ; + kindFallback = ; + }; + nameFallback = { + kindSpecific. = ; + kindFallback = ; + }; + }; + + Returns both the structure itself for inspection and a function that can query it with very few allocations/lookups + + This allows collapsing arbitrarily many problem handlers/matchers into a predictable structure that can be queried in a predictable and fast way + */ + genHandlerSwitch = + config: + let + constraints = + # matchers have low priority + map (m: m // { priority = 0; }) config.problems.matchers + # handlers have higher priority + ++ concatLists ( + mapAttrsToList ( + package: forPackage: + mapAttrsToList (name: handler: { + inherit package name handler; + kind = null; + priority = 1; + }) forPackage + ) config.problems.handlers + ); + + getHandler = + list: + (foldl' + (acc: el: { + priority = max acc.priority el.priority; + handler = + if acc.priority == el.priority then + handlers.max acc.handler el.handler + else if acc.priority > el.priority then + acc.handler + else + el.handler; + }) + { + priority = 0; + handler = "ignore"; + } + list + ).handler; + + identOrder = [ + "kind" + "name" + "package" + ]; + + doLevel = + index: + let + ident = elemAt identOrder index; + nextLevel = if index + 1 == length identOrder then getHandler else doLevel (index + 1); + in + list: + let + # Partition all matchers into ident-specific (.wrong) and -unspecific (.right) ones + parted = partition (m: isNull m.${ident}) list; + # We only use the unspecific ones to compute the fallback + fallback = nextLevel parted.right; + specific = pipe parted.wrong [ + (groupBy (m: m.${ident})) + # For ident-specific handlers, the unspecific ones also apply + (mapAttrs (package: handlers: nextLevel (handlers ++ parted.right))) + # Memory optimisation: Don't need a specific handler if it would end up the same as the fallback + (filterAttrs (name: res: res != fallback)) + ]; + in + # Optimisation in case it's always the same handler, + # can propagate up for the entire switch to just be a string + if specific == { } && isString fallback then + fallback + else + { + "${ident}Fallback" = fallback; + "${ident}Specific" = specific; + }; + switch = doLevel 0 constraints; + in + { + inherit switch; + handlerForProblem = + if isString switch then + pname: name: kind: + switch + else + pname: name: kind: + let + switch' = switch.kindSpecific.${kind} or switch.kindFallback; + in + if isString switch' then + switch' + else + let + switch'' = switch'.nameSpecific.${name} or switch'.nameFallback; + in + if isString switch'' then + switch'' + else + switch''.packageSpecific.${pname} or switch''.packageFallback; + }; + + genCheckProblems = + config: + let + # This is here so that it gets cached for a (checkProblems config) thunk + inherit (genHandlerSwitch config) + handlerForProblem + ; + in + attrs: + let + pname = getName attrs; + manualProblems = attrs.meta.problems or { }; + in + if + # Fast path for when there's no problem that needs to be handled + # No automatic problems that needs handling + all ( + problem: + problem.condition attrs -> handlerForProblem pname problem.kindName problem.kindName == "ignore" + ) automaticProblems + && ( + # No manual problems + manualProblems == { } + # Or all manual problems are ignored + || all (name: handlerForProblem pname name (manualProblems.${name}.kind or name) == "ignore") ( + attrNames manualProblems + ) + ) + then + null + else + # Slow path, only here we actually figure out which problems we need to handle + let + problems = attrs.meta.problems or { } // genAutomaticProblems attrs; + problemsToHandle = filter (v: v.handler != "ignore") ( + mapAttrsToList (name: problem: rec { + inherit name; + # Kind falls back to the name + kind = problem.kind or name; + handler = handlerForProblem pname name kind; + inherit problem; + }) problems + ); + in + processProblems pname problemsToHandle; + + processProblems = + pname: problemsToHandle: + let + grouped = groupBy (v: v.handler) problemsToHandle; + + warnProblems = grouped.warn or [ ]; + errorProblems = grouped.error or [ ]; + + # assert annotatedProblems != [ ]; + fullMessage = + v: + "${v.name}${optionalString (v.kind != v.name) " (kind \"${v.kind}\")"}: " + + "${v.problem.message}${ + optionalString (v.problem.urls or [ ] != [ ]) " (${concatStringsSep ", " v.problem.urls})" + }"; + + warnings = map (x: { + reason = "problem"; + msg = "has the following problem: ${fullMessage x}"; + remediation = "See https://nixos.org/manual/nixpkgs/unstable#sec-problems"; # TODO: Add remediation, maybe just link to docs to keep it small + }) warnProblems; + + error = + if errorProblems == [ ] then + null + else + { + msg = '' + has problems: + ${concatMapStringsSep "\n" (x: "- ${fullMessage x}") errorProblems} + ''; + ## TODO: Add mention of problem.matchers, or maybe better link to docs of that + remediation = '' + 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 = { + ${concatMapStringsSep "\n " ( + problem: + ''${escapeNixIdentifier pname}.${escapeNixIdentifier problem.name} = "warn"; # or "ignore"'' + ) errorProblems} + }; + } + ''; + }; + in + { + inherit error warnings; + }; + +} diff --git a/pkgs/test/default.nix b/pkgs/test/default.nix index efcd4963136d..acd0982ed5f8 100644 --- a/pkgs/test/default.nix +++ b/pkgs/test/default.nix @@ -187,6 +187,11 @@ in texlive = recurseIntoAttrs (callPackage ./texlive { }); + # TODO: Temporarily disabled recursion so we can see the performance comparison in the PR, + # which only runs if there's exactly the same packages before and after, and this would add packages + #problems = recurseIntoAttrs (callPackage ./problems { }); + problems = callPackage ./problems { }; + cuda = callPackage ./cuda { }; trivial-builders = callPackage ../build-support/trivial-builders/test/default.nix { }; diff --git a/pkgs/test/problems/cases/deprecated-and-removal-and-maintainerless-error/default.nix b/pkgs/test/problems/cases/deprecated-and-removal-and-maintainerless-error/default.nix new file mode 100644 index 000000000000..496c562890a5 --- /dev/null +++ b/pkgs/test/problems/cases/deprecated-and-removal-and-maintainerless-error/default.nix @@ -0,0 +1,24 @@ +{ nixpkgs }: +let + pkgs = import nixpkgs { + system = "x86_64-linux"; + overlays = [ ]; + config = { + problems.handlers = { + "a"."deprecated" = "error"; + "a"."removal" = "error"; + "a"."maintainerless" = "error"; + }; + }; + }; +in +pkgs.stdenvNoCC.mkDerivation { + pname = "a"; + version = "0"; + meta.description = "Some package"; + meta.problems = { + deprecated.message = "Package is deprecated and replaced by b."; + removal.message = "Package will be removed."; + }; + meta.maintainers = [ ]; +} diff --git a/pkgs/test/problems/cases/deprecated-and-removal-and-maintainerless-error/expected-stderr b/pkgs/test/problems/cases/deprecated-and-removal-and-maintainerless-error/expected-stderr new file mode 100644 index 000000000000..35fc25e10a98 --- /dev/null +++ b/pkgs/test/problems/cases/deprecated-and-removal-and-maintainerless-error/expected-stderr @@ -0,0 +1,20 @@ +(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:18 because it has problems: +- deprecated: Package is deprecated and replaced by b. +- maintainerless: This package has no declared maintainer, i.e. an empty `meta.maintainers` and `meta.teams` attribute. +- removal: Package will be removed. + +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.deprecated = "warn"; # or "ignore" + a.maintainerless = "warn"; # or "ignore" + a.removal = "warn"; # or "ignore" + }; + } diff --git a/pkgs/test/problems/cases/deprecated-and-removal-error/default.nix b/pkgs/test/problems/cases/deprecated-and-removal-error/default.nix new file mode 100644 index 000000000000..20a18cc17c78 --- /dev/null +++ b/pkgs/test/problems/cases/deprecated-and-removal-error/default.nix @@ -0,0 +1,22 @@ +{ nixpkgs }: +let + pkgs = import nixpkgs { + system = "x86_64-linux"; + overlays = [ ]; + config = { + problems.handlers = { + "a"."deprecated" = "error"; + "a"."removal" = "error"; + }; + }; + }; +in +pkgs.stdenvNoCC.mkDerivation { + pname = "a"; + version = "0"; + meta.problems = { + deprecated.message = "Package is deprecated and replaced by b"; + removal.message = "Package will be removed"; + }; + meta.maintainers = [ "hello" ]; +} diff --git a/pkgs/test/problems/cases/deprecated-and-removal-error/expected-stderr b/pkgs/test/problems/cases/deprecated-and-removal-error/expected-stderr new file mode 100644 index 000000000000..f23f10373fc3 --- /dev/null +++ b/pkgs/test/problems/cases/deprecated-and-removal-error/expected-stderr @@ -0,0 +1,18 @@ +(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: +- deprecated: Package is deprecated and replaced by b +- removal: Package will be removed + +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.deprecated = "warn"; # or "ignore" + a.removal = "warn"; # or "ignore" + }; + } diff --git a/pkgs/test/problems/cases/deprecated-error/default.nix b/pkgs/test/problems/cases/deprecated-error/default.nix new file mode 100644 index 000000000000..405d394bd893 --- /dev/null +++ b/pkgs/test/problems/cases/deprecated-error/default.nix @@ -0,0 +1,20 @@ +{ nixpkgs }: +let + pkgs = import nixpkgs { + system = "x86_64-linux"; + overlays = [ ]; + config = { + problems.handlers = { + "a"."deprecated" = "error"; + }; + }; + }; +in +pkgs.stdenvNoCC.mkDerivation { + pname = "a"; + version = "0"; + meta.problems.deprecated = { + message = "Package is deprecated and replaced by b"; + }; + meta.maintainers = [ "hello" ]; +} diff --git a/pkgs/test/problems/cases/deprecated-error/expected-stderr b/pkgs/test/problems/cases/deprecated-error/expected-stderr new file mode 100644 index 000000000000..6eba972e8775 --- /dev/null +++ b/pkgs/test/problems/cases/deprecated-error/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:15 because it has problems: +- deprecated: Package is deprecated and replaced by b + +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.deprecated = "warn"; # or "ignore" + }; + } diff --git a/pkgs/test/problems/cases/deprecated-ignore/default.nix b/pkgs/test/problems/cases/deprecated-ignore/default.nix new file mode 100644 index 000000000000..0ce36d294842 --- /dev/null +++ b/pkgs/test/problems/cases/deprecated-ignore/default.nix @@ -0,0 +1,18 @@ +{ nixpkgs }: +let + pkgs = import nixpkgs { + system = "x86_64-linux"; + overlays = [ ]; + config = { + problems.handlers = { + "a"."deprecated" = "ignore"; + }; + }; + }; +in +pkgs.stdenvNoCC.mkDerivation { + pname = "a"; + version = "0"; + meta.problems.deprecated.message = "Package is deprecated and is replaced by b."; + meta.maintainers = [ "hello" ]; +} diff --git a/pkgs/test/problems/cases/deprecated-ignore/expected-stderr b/pkgs/test/problems/cases/deprecated-ignore/expected-stderr new file mode 100644 index 000000000000..c255c1d5a32b --- /dev/null +++ b/pkgs/test/problems/cases/deprecated-ignore/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/deprecated-warn/default.nix b/pkgs/test/problems/cases/deprecated-warn/default.nix new file mode 100644 index 000000000000..bcafc3e96f6a --- /dev/null +++ b/pkgs/test/problems/cases/deprecated-warn/default.nix @@ -0,0 +1,18 @@ +{ nixpkgs }: +let + pkgs = import nixpkgs { + system = "x86_64-linux"; + overlays = [ ]; + config = { + problems.handlers = { + "a"."deprecated" = "warn"; + }; + }; + }; +in +pkgs.stdenvNoCC.mkDerivation { + pname = "a"; + version = "0"; + meta.problems.deprecated.message = "Package a is deprecated and superseeded by b."; + meta.maintainers = [ "hello" ]; +} diff --git a/pkgs/test/problems/cases/deprecated-warn/expected-stderr b/pkgs/test/problems/cases/deprecated-warn/expected-stderr new file mode 100644 index 000000000000..c0cd1ab06bb6 --- /dev/null +++ b/pkgs/test/problems/cases/deprecated-warn/expected-stderr @@ -0,0 +1,2 @@ +evaluation warning: Package 'a-0' in /nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-default.nix:15 has the following problem: deprecated: Package a is deprecated and superseeded by b. See https://nixos.org/manual/nixpkgs/unstable#sec-problems +warning: you did not specify '--add-root'; the result might be removed by the garbage collector diff --git a/pkgs/test/problems/cases/invalid-kind-error/default.nix b/pkgs/test/problems/cases/invalid-kind-error/default.nix new file mode 100644 index 000000000000..acb4d9e8b43e --- /dev/null +++ b/pkgs/test/problems/cases/invalid-kind-error/default.nix @@ -0,0 +1,15 @@ +{ nixpkgs }: +let + pkgs = import nixpkgs { + system = "x86_64-linux"; + overlays = [ ]; + config.checkMeta = true; + }; +in +pkgs.stdenvNoCC.mkDerivation { + pname = "a"; + version = "0"; + meta.problems = { + invalid.message = "No maintainers"; + }; +} diff --git a/pkgs/test/problems/cases/invalid-kind-error/expected-stderr b/pkgs/test/problems/cases/invalid-kind-error/expected-stderr new file mode 100644 index 000000000000..dbe13602c3f8 --- /dev/null +++ b/pkgs/test/problems/cases/invalid-kind-error/expected-stderr @@ -0,0 +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` diff --git a/pkgs/test/problems/cases/maintainerless-and-removal-and-deprecated-warn/default.nix b/pkgs/test/problems/cases/maintainerless-and-removal-and-deprecated-warn/default.nix new file mode 100644 index 000000000000..6c58ecf5599c --- /dev/null +++ b/pkgs/test/problems/cases/maintainerless-and-removal-and-deprecated-warn/default.nix @@ -0,0 +1,24 @@ +{ nixpkgs }: +let + pkgs = import nixpkgs { + system = "x86_64-linux"; + overlays = [ ]; + config = { + problems.handlers = { + "a"."maintainerless" = "warn"; + "a"."deprecated" = "warn"; + "a"."removal" = "warn"; + }; + }; + }; +in +pkgs.stdenvNoCC.mkDerivation { + pname = "a"; + version = "0"; + meta.description = "Some package"; + meta.maintainers = [ ]; + meta.problems = { + removal.message = "Package to be removed."; + deprecated.message = "Package will be deprecated."; + }; +} diff --git a/pkgs/test/problems/cases/maintainerless-and-removal-and-deprecated-warn/expected-stderr b/pkgs/test/problems/cases/maintainerless-and-removal-and-deprecated-warn/expected-stderr new file mode 100644 index 000000000000..2170efd4cddb --- /dev/null +++ b/pkgs/test/problems/cases/maintainerless-and-removal-and-deprecated-warn/expected-stderr @@ -0,0 +1,4 @@ +evaluation warning: Package 'a-0' in /nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-default.nix:18 has the following problem: deprecated: Package will be deprecated. See https://nixos.org/manual/nixpkgs/unstable#sec-problems +evaluation warning: Package 'a-0' in /nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-default.nix:18 has the following problem: maintainerless: This package has no declared maintainer, i.e. an empty `meta.maintainers` and `meta.teams` attribute. See https://nixos.org/manual/nixpkgs/unstable#sec-problems +evaluation warning: Package 'a-0' in /nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-default.nix:18 has the following problem: removal: Package to be removed. See https://nixos.org/manual/nixpkgs/unstable#sec-problems +warning: you did not specify '--add-root'; the result might be removed by the garbage collector diff --git a/pkgs/test/problems/cases/maintainerless-and-removal-warn/default.nix b/pkgs/test/problems/cases/maintainerless-and-removal-warn/default.nix new file mode 100644 index 000000000000..7d672a89f7c5 --- /dev/null +++ b/pkgs/test/problems/cases/maintainerless-and-removal-warn/default.nix @@ -0,0 +1,20 @@ +{ nixpkgs }: +let + pkgs = import nixpkgs { + system = "x86_64-linux"; + overlays = [ ]; + config = { + problems.handlers = { + "a"."maintainerless" = "warn"; + "a"."removal" = "warn"; + }; + }; + }; +in +pkgs.stdenvNoCC.mkDerivation { + pname = "a"; + version = "0"; + meta.description = "Some package"; + meta.maintainers = [ ]; + meta.problems.removal.message = "Package will be removed."; +} diff --git a/pkgs/test/problems/cases/maintainerless-and-removal-warn/expected-stderr b/pkgs/test/problems/cases/maintainerless-and-removal-warn/expected-stderr new file mode 100644 index 000000000000..0a6a99fd9d14 --- /dev/null +++ b/pkgs/test/problems/cases/maintainerless-and-removal-warn/expected-stderr @@ -0,0 +1,3 @@ +evaluation warning: Package 'a-0' in /nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-default.nix:17 has the following problem: maintainerless: This package has no declared maintainer, i.e. an empty `meta.maintainers` and `meta.teams` attribute. See https://nixos.org/manual/nixpkgs/unstable#sec-problems +evaluation warning: Package 'a-0' in /nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-default.nix:17 has the following problem: removal: Package will be removed. See https://nixos.org/manual/nixpkgs/unstable#sec-problems +warning: you did not specify '--add-root'; the result might be removed by the garbage collector diff --git a/pkgs/test/problems/cases/maintainerless-error/default.nix b/pkgs/test/problems/cases/maintainerless-error/default.nix new file mode 100644 index 000000000000..e925b45e0862 --- /dev/null +++ b/pkgs/test/problems/cases/maintainerless-error/default.nix @@ -0,0 +1,18 @@ +{ nixpkgs }: +let + pkgs = import nixpkgs { + system = "x86_64-linux"; + overlays = [ ]; + config = { + problems.handlers = { + "a"."maintainerless" = "error"; + }; + }; + }; +in +pkgs.stdenvNoCC.mkDerivation { + pname = "a"; + version = "0"; + meta.description = "Some package"; + meta.maintainers = [ ]; +} diff --git a/pkgs/test/problems/cases/maintainerless-error/expected-stderr b/pkgs/test/problems/cases/maintainerless-error/expected-stderr new file mode 100644 index 000000000000..9d98ce9e40fb --- /dev/null +++ b/pkgs/test/problems/cases/maintainerless-error/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: +- maintainerless: This package has no declared maintainer, i.e. an empty `meta.maintainers` and `meta.teams` attribute. + +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.maintainerless = "warn"; # or "ignore" + }; + } diff --git a/pkgs/test/problems/cases/maintainerless-ignore/default.nix b/pkgs/test/problems/cases/maintainerless-ignore/default.nix new file mode 100644 index 000000000000..8531510073d4 --- /dev/null +++ b/pkgs/test/problems/cases/maintainerless-ignore/default.nix @@ -0,0 +1,17 @@ +{ nixpkgs }: +let + pkgs = import nixpkgs { + system = "x86_64-linux"; + overlays = [ ]; + config = { + problems.handlers = { + "a"."maintainerless" = "ignore"; + }; + }; + }; +in +pkgs.stdenvNoCC.mkDerivation { + pname = "a"; + version = "0"; + meta.maintainers = [ ]; +} diff --git a/pkgs/test/problems/cases/maintainerless-ignore/expected-stderr b/pkgs/test/problems/cases/maintainerless-ignore/expected-stderr new file mode 100644 index 000000000000..c255c1d5a32b --- /dev/null +++ b/pkgs/test/problems/cases/maintainerless-ignore/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/maintainerless-warn/default.nix b/pkgs/test/problems/cases/maintainerless-warn/default.nix new file mode 100644 index 000000000000..abaf4c5c2b16 --- /dev/null +++ b/pkgs/test/problems/cases/maintainerless-warn/default.nix @@ -0,0 +1,18 @@ +{ nixpkgs }: +let + pkgs = import nixpkgs { + system = "x86_64-linux"; + overlays = [ ]; + config = { + problems.handlers = { + "a"."maintainerless" = "warn"; + }; + }; + }; +in +pkgs.stdenvNoCC.mkDerivation { + pname = "a"; + version = "0"; + meta.description = "Some package"; + meta.maintainers = [ ]; +} diff --git a/pkgs/test/problems/cases/maintainerless-warn/expected-stderr b/pkgs/test/problems/cases/maintainerless-warn/expected-stderr new file mode 100644 index 000000000000..70a9385eec00 --- /dev/null +++ b/pkgs/test/problems/cases/maintainerless-warn/expected-stderr @@ -0,0 +1,2 @@ +evaluation warning: Package 'a-0' in /nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-default.nix:16 has the following problem: maintainerless: This package has no declared maintainer, i.e. an empty `meta.maintainers` and `meta.teams` attribute. See https://nixos.org/manual/nixpkgs/unstable#sec-problems +warning: you did not specify '--add-root'; the result might be removed by the garbage collector diff --git a/pkgs/test/problems/cases/no-message/default.nix b/pkgs/test/problems/cases/no-message/default.nix new file mode 100644 index 000000000000..bc89ad823f5d --- /dev/null +++ b/pkgs/test/problems/cases/no-message/default.nix @@ -0,0 +1,22 @@ +{ nixpkgs }: +let + pkgs = import nixpkgs { + system = "x86_64-linux"; + overlays = [ ]; + config = { + checkMeta = true; + problems.matchers = [ + { + package = "a"; + handler = "error"; + } + ]; + }; + }; +in +pkgs.stdenvNoCC.mkDerivation { + pname = "a"; + version = "0"; + meta.description = "Some package"; + meta.problems.removal = { }; +} diff --git a/pkgs/test/problems/cases/no-message/expected-stderr b/pkgs/test/problems/cases/no-message/expected-stderr new file mode 100644 index 000000000000..d40bf1e17280 --- /dev/null +++ b/pkgs/test/problems/cases/no-message/expected-stderr @@ -0,0 +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:20 because it has an invalid meta attrset: + - a.meta.problems.removal: `.message` not specified diff --git a/pkgs/test/problems/cases/no-problems-description/default.nix b/pkgs/test/problems/cases/no-problems-description/default.nix new file mode 100644 index 000000000000..66a201eab403 --- /dev/null +++ b/pkgs/test/problems/cases/no-problems-description/default.nix @@ -0,0 +1,19 @@ +{ nixpkgs }: +let + pkgs = import nixpkgs { + system = "x86_64-linux"; + overlays = [ ]; + config = { + problems.matchers = [ + { + package = "a"; + handler = "error"; + } + ]; + }; + }; +in +pkgs.stdenvNoCC.mkDerivation { + pname = "a"; + version = "0"; +} diff --git a/pkgs/test/problems/cases/no-problems-description/expected-stderr b/pkgs/test/problems/cases/no-problems-description/expected-stderr new file mode 100644 index 000000000000..c255c1d5a32b --- /dev/null +++ b/pkgs/test/problems/cases/no-problems-description/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/no-problems-fod/default.nix b/pkgs/test/problems/cases/no-problems-fod/default.nix new file mode 100644 index 000000000000..5b7cb9fdb202 --- /dev/null +++ b/pkgs/test/problems/cases/no-problems-fod/default.nix @@ -0,0 +1,21 @@ +{ nixpkgs }: +let + pkgs = import nixpkgs { + system = "x86_64-linux"; + overlays = [ ]; + config = { + problems.matchers = [ + { + package = "a"; + handler = "error"; + } + ]; + }; + }; +in +pkgs.stdenvNoCC.mkDerivation { + pname = "a"; + version = "0"; + meta.description = "Some package"; + outputHash = pkgs.lib.fakeHash; +} diff --git a/pkgs/test/problems/cases/no-problems-fod/expected-stderr b/pkgs/test/problems/cases/no-problems-fod/expected-stderr new file mode 100644 index 000000000000..c255c1d5a32b --- /dev/null +++ b/pkgs/test/problems/cases/no-problems-fod/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/no-problems-team-maintainer/default.nix b/pkgs/test/problems/cases/no-problems-team-maintainer/default.nix new file mode 100644 index 000000000000..605bf0c2f696 --- /dev/null +++ b/pkgs/test/problems/cases/no-problems-team-maintainer/default.nix @@ -0,0 +1,21 @@ +{ nixpkgs }: +let + pkgs = import nixpkgs { + system = "x86_64-linux"; + overlays = [ ]; + config = { + problems.matchers = [ + { + package = "a"; + handler = "error"; + } + ]; + }; + }; +in +pkgs.stdenvNoCC.mkDerivation { + pname = "a"; + version = "0"; + meta.teams = [ "hello" ]; + meta.description = "Some package"; +} diff --git a/pkgs/test/problems/cases/no-problems-team-maintainer/expected-stderr b/pkgs/test/problems/cases/no-problems-team-maintainer/expected-stderr new file mode 100644 index 000000000000..c255c1d5a32b --- /dev/null +++ b/pkgs/test/problems/cases/no-problems-team-maintainer/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/no-problems/default.nix b/pkgs/test/problems/cases/no-problems/default.nix new file mode 100644 index 000000000000..2780c964ae4b --- /dev/null +++ b/pkgs/test/problems/cases/no-problems/default.nix @@ -0,0 +1,21 @@ +{ nixpkgs }: +let + pkgs = import nixpkgs { + system = "x86_64-linux"; + overlays = [ ]; + config = { + problems.matchers = [ + { + package = "a"; + handler = "error"; + } + ]; + }; + }; +in +pkgs.stdenvNoCC.mkDerivation { + pname = "a"; + version = "0"; + meta.maintainers = [ "hello" ]; + meta.description = "Some package"; +} diff --git a/pkgs/test/problems/cases/no-problems/expected-stderr b/pkgs/test/problems/cases/no-problems/expected-stderr new file mode 100644 index 000000000000..c255c1d5a32b --- /dev/null +++ b/pkgs/test/problems/cases/no-problems/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/non-attrs-problem/default.nix b/pkgs/test/problems/cases/non-attrs-problem/default.nix new file mode 100644 index 000000000000..167630e49314 --- /dev/null +++ b/pkgs/test/problems/cases/non-attrs-problem/default.nix @@ -0,0 +1,21 @@ +{ nixpkgs }: +let + pkgs = import nixpkgs { + system = "x86_64-linux"; + overlays = [ ]; + config = { + checkMeta = true; + problems.matchers = [ + { + package = "a"; + handler = "error"; + } + ]; + }; + }; +in +pkgs.stdenvNoCC.mkDerivation { + pname = "a"; + version = "0"; + meta.problems.florp = true; +} diff --git a/pkgs/test/problems/cases/non-attrs-problem/expected-stderr b/pkgs/test/problems/cases/non-attrs-problem/expected-stderr new file mode 100644 index 000000000000..f4abb09ff994 --- /dev/null +++ b/pkgs/test/problems/cases/non-attrs-problem/expected-stderr @@ -0,0 +1,5 @@ +(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:19 because it has an invalid meta attrset: + - a.meta.problems.florp: Invalid value; expected attrs, got + true diff --git a/pkgs/test/problems/cases/package-name-matcher/default.nix b/pkgs/test/problems/cases/package-name-matcher/default.nix new file mode 100644 index 000000000000..570c9f6f389c --- /dev/null +++ b/pkgs/test/problems/cases/package-name-matcher/default.nix @@ -0,0 +1,20 @@ +{ nixpkgs }: +let + pkgs = import nixpkgs { + system = "x86_64-linux"; + overlays = [ ]; + config = { + problems.matchers = [ + { + package = "a"; + name = "b"; + handler = "error"; + } + ]; + }; + }; +in +pkgs.stdenvNoCC.mkDerivation { + pname = "a"; + version = "0"; +} diff --git a/pkgs/test/problems/cases/package-name-matcher/expected-stderr b/pkgs/test/problems/cases/package-name-matcher/expected-stderr new file mode 100644 index 000000000000..9e38189c515f --- /dev/null +++ b/pkgs/test/problems/cases/package-name-matcher/expected-stderr @@ -0,0 +1,5 @@ +(stack trace truncated; use '--show-trace' to show the full, detailed trace) + +error: Failed assertions: +- There is a problems.matchers with `package = "a"` and `name = "b". Use the following instead: + problems.handlers.a.b = "error"; diff --git a/pkgs/test/problems/cases/problem-urls/default.nix b/pkgs/test/problems/cases/problem-urls/default.nix new file mode 100644 index 000000000000..1674e046caf9 --- /dev/null +++ b/pkgs/test/problems/cases/problem-urls/default.nix @@ -0,0 +1,28 @@ +{ nixpkgs }: +let + pkgs = import nixpkgs { + system = "x86_64-linux"; + overlays = [ ]; + config = { + problems.matchers = [ + { + package = "a"; + handler = "error"; + } + ]; + }; + }; +in +pkgs.stdenvNoCC.mkDerivation { + pname = "a"; + version = "0"; + meta.maintainers = [ "hello" ]; + meta.description = "Some package"; + meta.problems.removal = { + message = "Removed because of XYZ."; + urls = [ + "https://example.com" + "https://anotherexample.com" + ]; + }; +} diff --git a/pkgs/test/problems/cases/problem-urls/expected-stderr b/pkgs/test/problems/cases/problem-urls/expected-stderr new file mode 100644 index 000000000000..e27b57297ef0 --- /dev/null +++ b/pkgs/test/problems/cases/problem-urls/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:20 because it has problems: +- removal: Removed because of XYZ. (https://example.com, https://anotherexample.com) + +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.removal = "warn"; # or "ignore" + }; + } diff --git a/pkgs/test/problems/cases/removal-error/default.nix b/pkgs/test/problems/cases/removal-error/default.nix new file mode 100644 index 000000000000..5fd17d518612 --- /dev/null +++ b/pkgs/test/problems/cases/removal-error/default.nix @@ -0,0 +1,17 @@ +{ nixpkgs }: +let + pkgs = import nixpkgs { + system = "x86_64-linux"; + overlays = [ ]; + config = { + problems.handlers.a.removal = "error"; + }; + }; +in +pkgs.stdenvNoCC.mkDerivation { + pname = "a"; + version = "0"; + meta.problems = { + removal.message = "This package has been abandoned upstream and will be removed."; + }; +} diff --git a/pkgs/test/problems/cases/removal-error/expected-stderr b/pkgs/test/problems/cases/removal-error/expected-stderr new file mode 100644 index 000000000000..9d60a973e552 --- /dev/null +++ b/pkgs/test/problems/cases/removal-error/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:13 because it has problems: +- removal: This package has been abandoned upstream and will be removed. + +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.removal = "warn"; # or "ignore" + }; + } diff --git a/pkgs/test/problems/cases/removal-ignore/default.nix b/pkgs/test/problems/cases/removal-ignore/default.nix new file mode 100644 index 000000000000..0cccb8873b27 --- /dev/null +++ b/pkgs/test/problems/cases/removal-ignore/default.nix @@ -0,0 +1,17 @@ +{ nixpkgs }: +let + pkgs = import nixpkgs { + system = "x86_64-linux"; + overlays = [ ]; + config = { + problems.handlers = { + "a"."removal" = "ignore"; + }; + }; + }; +in +pkgs.stdenvNoCC.mkDerivation { + pname = "a"; + version = "0"; + meta.problems.removal.message = "To be removed"; +} diff --git a/pkgs/test/problems/cases/removal-ignore/expected-stderr b/pkgs/test/problems/cases/removal-ignore/expected-stderr new file mode 100644 index 000000000000..c255c1d5a32b --- /dev/null +++ b/pkgs/test/problems/cases/removal-ignore/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/removal-twice-error/default.nix b/pkgs/test/problems/cases/removal-twice-error/default.nix new file mode 100644 index 000000000000..7898a9ae30ee --- /dev/null +++ b/pkgs/test/problems/cases/removal-twice-error/default.nix @@ -0,0 +1,22 @@ +{ nixpkgs }: +let + pkgs = import nixpkgs { + system = "x86_64-linux"; + overlays = [ ]; + config.checkMeta = true; + }; +in +pkgs.stdenvNoCC.mkDerivation { + pname = "a"; + version = "0"; + meta.description = "Some package"; + meta.problems = { + removal = { + message = "This package has been abandoned upstream and will be removed"; + }; + removal2 = { + kind = "removal"; + message = "This package has been abandoned upstream and will be removed2"; + }; + }; +} diff --git a/pkgs/test/problems/cases/removal-twice-error/expected-stderr b/pkgs/test/problems/cases/removal-twice-error/expected-stderr new file mode 100644 index 000000000000..86177f4c31f4 --- /dev/null +++ b/pkgs/test/problems/cases/removal-twice-error/expected-stderr @@ -0,0 +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:12 because it has an invalid meta attrset: + - a.meta.problems: Problem kind removal should be unique, but is used for these problems: removal, removal2 diff --git a/pkgs/test/problems/cases/removal-warn/default.nix b/pkgs/test/problems/cases/removal-warn/default.nix new file mode 100644 index 000000000000..60475d75591a --- /dev/null +++ b/pkgs/test/problems/cases/removal-warn/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.maintainers = [ "hello" ]; + meta.problems.removal.message = "Package to be removed."; +} diff --git a/pkgs/test/problems/cases/removal-warn/expected-stderr b/pkgs/test/problems/cases/removal-warn/expected-stderr new file mode 100644 index 000000000000..619e727b9f1b --- /dev/null +++ b/pkgs/test/problems/cases/removal-warn/expected-stderr @@ -0,0 +1,2 @@ +evaluation warning: Package 'a-0' in /nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-default.nix:11 has the following problem: removal: Package to be removed. See https://nixos.org/manual/nixpkgs/unstable#sec-problems +warning: you did not specify '--add-root'; the result might be removed by the garbage collector diff --git a/pkgs/test/problems/default.nix b/pkgs/test/problems/default.nix new file mode 100644 index 000000000000..1e2ce5108bbe --- /dev/null +++ b/pkgs/test/problems/default.nix @@ -0,0 +1,60 @@ +{ + lib, + nix, + runCommand, + removeReferencesTo, + path, + gitMinimal, +}: +let + nixpkgs = lib.cleanSource path; + unitResult = import ./unit.nix { inherit lib; }; +in +assert lib.assertMsg (unitResult == [ ]) + "problems/unit.nix failing: ${lib.generators.toPretty { } unitResult}"; +lib.mapAttrs ( + name: _: + let + nixFile = "${./cases + "/${name}/default.nix"}"; + result = runCommand "test-problems-${name}" { nativeBuildInputs = [ removeReferencesTo ]; } '' + export NIX_STATE_DIR=$(mktemp -d) + mkdir $out + + 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" + ${nixFile} + # Readonly mode because we don't need to write anything to the store + "--readonly-mode" + "--arg" + "nixpkgs" + "${nixpkgs}" + ) + + echo "''${command[*]@Q}" > $out/command + echo "Running ''${command[*]@Q}" + set +e + "''${command[@]}" >$out/stdout 2>$out/stderr + set +e + echo "$?" > $out/code + echo "Command exited with code $(<$out/code)" + remove-references-to -t ${nixFile} $out/stderr + if grep '(stack trace truncated.*)' $out/stderr; then + sed -i -n '/(stack trace truncated.*)/,$ p' $out/stderr + fi + sed -i 's/^ //' $out/stderr + ''; + checker = runCommand "test-problems-check-${name}" { } '' + if ! PAGER=cat ${lib.getExe gitMinimal} diff --no-index ${ + ./cases + "/${name}/expected-stderr" + } ${result}/stderr ; then + echo "Output of $(< ${result}/command) does not match what was expected. To adapt:" + echo "cp ${result}/stderr ${lib.path.removePrefix path ./cases + "/${name}/expected-stderr"}" + exit 1 + fi + touch $out + ''; + in + checker +) (builtins.readDir ./cases) diff --git a/pkgs/test/problems/unit.nix b/pkgs/test/problems/unit.nix new file mode 100644 index 000000000000..073ddaa12cfa --- /dev/null +++ b/pkgs/test/problems/unit.nix @@ -0,0 +1,153 @@ +{ lib }: +let + p = import ../../stdenv/generic/problems.nix { inherit lib; }; + + genHandlerTest = + let + slowReference = + config: package: name: kind: + # Try to find an explicit handler + (config.problems.handlers.${package} or { }).${name} + # Fall back, iterating through the matchers + or (lib.pipe config.problems.matchers [ + # Find matches + (lib.filter ( + matcher: + (matcher.name != null -> name == matcher.name) + && (matcher.kind != null -> kind == matcher.kind) + && (matcher.package != null -> package == matcher.package) + )) + # Extract handler level + (map (matcher: matcher.handler)) + # Take the strongest matched handler level + (lib.foldl' p.handlers.max "ignore") + ]); + + genValue = + f: + map + ( + package: + map + ( + name: + map (kind: f package name kind) [ + "k1" + "k2" + "k3" + ] + ) + [ + "n1" + "n2" + "n3" + ] + ) + [ + "p1" + "p2" + "p3" + ]; + + in + v: { + expr = genValue (p.genHandlerSwitch { problems = v; }).handlerForProblem; + expected = genValue (slowReference { + problems = v; + }); + }; +in +lib.runTests { + testHandlersLessThan = + let + levels = p.handlers.levels; + slowReference = + a: b: + lib.lists.findFirstIndex (v: v == a) (abort "Shouldn't happen") levels + < lib.lists.findFirstIndex (v: v == b) (abort "Shouldn't happen") levels; + + genValue = + f: + lib.genList ( + i: lib.genList (j: f (lib.elemAt levels i) (lib.elemAt levels j)) (lib.length levels) + ) (lib.length levels); + in + { + expr = genValue p.handlers.lessThan; + expected = genValue slowReference; + }; + + testHandlerEmpty = genHandlerTest { + matchers = [ ]; + handlers = { }; + }; + + testHandlerNameSpecificHandlers = genHandlerTest { + matchers = [ ]; + handlers.p1.n1 = "error"; + handlers.p1.n2 = "warn"; + handlers.p1.n3 = "ignore"; + }; + + testHandlerPackageSpecificHandlers = genHandlerTest { + matchers = [ ]; + handlers.p1.n1 = "error"; + handlers.p2.n1 = "warn"; + handlers.p3.n1 = "ignore"; + }; + + testHandlersOverrideMatchers = genHandlerTest { + matchers = [ + { + package = "p1"; + name = "n1"; + kind = null; + handler = "error"; + } + ]; + handlers.p1.n1 = "warn"; + }; + + testMatchersDefault = genHandlerTest { + matchers = [ + # Everything should warn by default + { + package = null; + name = null; + kind = null; + handler = "warn"; + } + ]; + handlers = { }; + }; + + testMatchersComplicated = genHandlerTest { + matchers = [ + { + package = "p1"; + name = null; + kind = null; + handler = "warn"; + } + { + package = "p1"; + name = "n1"; + kind = null; + handler = "error"; + } + { + package = "p1"; + name = null; + kind = "k1"; + handler = "error"; + } + { + package = "p1"; + name = "n2"; + kind = "k2"; + handler = "error"; + } + ]; + handlers = { }; + }; +} diff --git a/pkgs/top-level/config.nix b/pkgs/top-level/config.nix index 80d56c112e18..7efb30525420 100644 --- a/pkgs/top-level/config.nix +++ b/pkgs/top-level/config.nix @@ -45,11 +45,18 @@ let internal = true; }; + # Should be replaced by importing in the future + # see also https://github.com/NixOS/nixpkgs/pull/207187 warnings = mkOption { type = types.listOf types.str; default = [ ]; internal = true; }; + assertions = mkOption { + type = types.listOf types.anything; + default = [ ]; + internal = true; + }; # Config options @@ -448,6 +455,8 @@ let compatibility of configurations with 26.05. ''; }; + + problems = (import ../stdenv/generic/problems.nix { inherit lib; }).configOptions; }; in @@ -470,9 +479,35 @@ in inherit options; config = { - warnings = optionals config.warnUndeclaredOptions ( - mapAttrsToList (k: v: "undeclared Nixpkgs option set: config.${k}") config._undeclared or { } - ); + warnings = + optionals config.warnUndeclaredOptions ( + mapAttrsToList (k: v: "undeclared Nixpkgs option set: config.${k}") config._undeclared or { } + ) + ++ lib.optional (config.showDerivationWarnings != [ ]) '' + `config.showDerivationWarnings = [ "maintainerless" ]` is deprecated, use `config.problems` instead: + + config.problems.matchers = [ { kind = "maintainerless"; handler = "warn"; } ]; + + See this page for more details: https://nixos.org/manual/nixpkgs/unstable#sec-problems + ''; + + assertions = + # Collect the assertions from the problems.matchers.* submodules, propagate them into here + lib.concatMap (matcher: matcher.assertions) config.problems.matchers; + + # 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 = [ + # Be loud and clear about package removals + { + kind = "removal"; + handler = "warn"; + } + (lib.mkIf (lib.elem "maintainerless" config.showDerivationWarnings) { + kind = "maintainerless"; + handler = "warn"; + }) + ]; }; } diff --git a/pkgs/top-level/default.nix b/pkgs/top-level/default.nix index 29da5c93202c..607145be06aa 100644 --- a/pkgs/top-level/default.nix +++ b/pkgs/top-level/default.nix @@ -126,7 +126,16 @@ let }; # take all the rest as-is - config = lib.showWarnings configEval.config.warnings configEval.config; + config = + let + failedAssertionsString = lib.concatMapStringsSep "\n" (x: "- ${x.message}") ( + lib.filter (x: !x.assertion) configEval.config.assertions + ); + in + if failedAssertionsString != "" then + throw "Failed assertions:\n${failedAssertionsString}" + else + lib.showWarnings configEval.config.warnings configEval.config; # A few packages make a new package set to draw their dependencies from. # (Currently to get a cross tool chain, or forced-i686 package.) Rather than