From 339a83acc9c297bf604072e823bc4c85ce1c1403 Mon Sep 17 00:00:00 2001 From: piegames Date: Fri, 14 Jul 2023 21:08:32 +0200 Subject: [PATCH 1/3] RFC 127: Initial implementation --- doc/using/configuration.chapter.md | 53 ++++++++ pkgs/stdenv/generic/check-meta.nix | 212 +++++++++++++++++++++++++---- pkgs/stdenv/generic/problems.nix | 102 ++++++++++++++ pkgs/top-level/config.nix | 136 ++++++++++++++++-- pkgs/top-level/default.nix | 10 +- 5 files changed, 472 insertions(+), 41 deletions(-) create mode 100644 pkgs/stdenv/generic/problems.nix diff --git a/doc/using/configuration.chapter.md b/doc/using/configuration.chapter.md index 05a8fa5517cc3c8..4d002d430971ac3 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 in to 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} @@ -164,6 +166,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 = "error"; }`, 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 502343f01fb3200..12012043a0d7969 100644 --- a/pkgs/stdenv/generic/check-meta.nix +++ b/pkgs/stdenv/generic/check-meta.nix @@ -35,18 +35,27 @@ let availableOn ; + inherit (lib.generators) toPretty ; + inherit (import ./problems.nix { inherit lib; }) + problemKindsManual + problemKindsUnique' + ; + generateProblems = + (import ./problems.nix { inherit lib; }).generateProblems + { inherit hasNoMaintainers;} + config.problems; + # 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»"}"); + # Do not use this in new code, use `lib.getName` instead + getName = attrs: attrs.name or ("${attrs.pname or "«name-missing»"}-${attrs.version or "«version-missing»"}"); allowUnfree = config.allowUnfree || builtins.getEnv "NIXPKGS_ALLOW_UNFREE" == "1"; @@ -96,8 +105,7 @@ let hasUnfreeLicense = attrs: hasLicense attrs && isUnfree attrs.meta.license; - hasNoMaintainers = attrs: - attrs ? meta.maintainers && (length attrs.meta.maintainers) == 0; + hasNoMaintainers = pkg: lib.length (pkg.meta.maintainers or []) == 0; isMarkedBroken = attrs: attrs.meta.broken or false; @@ -131,7 +139,6 @@ let allowInsecurePredicate attrs || builtins.getEnv "NIXPKGS_ALLOW_INSECURE" == "1"; - isNonSource = sourceTypes: any (t: !t.isSource) sourceTypes; hasNonSourceProvenance = attrs: @@ -168,22 +175,26 @@ let unsupported = remediate_allowlist "UnsupportedSystem" (x: ""); blocklisted = x: ""; insecure = remediate_insecure; + problem = remediate_problem; broken-outputs = remediateOutputsToInstall; unknown-meta = x: ""; maintainerless = x: ""; }; + remediation_env_var = allow_attr: { Unfree = "NIXPKGS_ALLOW_UNFREE"; Broken = "NIXPKGS_ALLOW_BROKEN"; UnsupportedSystem = "NIXPKGS_ALLOW_UNSUPPORTED_SYSTEM"; NonSource = "NIXPKGS_ALLOW_NONSOURCE"; }.${allow_attr}; + remediation_phrase = allow_attr: { Unfree = "unfree packages"; Broken = "broken packages"; UnsupportedSystem = "packages that are unsupported for this system"; NonSource = "packages not built from source"; }.${allow_attr}; + remediate_predicate = predicateConfigAttr: attrs: '' @@ -194,11 +205,11 @@ let } ''; - # flakeNote will be printed in the remediation messages below. - flakeNote = " - Note: When using `nix shell`, `nix build`, `nix develop`, etc with a flake, - then pass `--impure` in order to allow use of environment variables. - "; + # flakeNote will be printed in the remediation messages below. + flakeNote = " + Note: When using `nix shell`, `nix build`, `nix develop`, etc with a flake, + then pass `--impure` in order to allow use of environment variables. + "; remediate_allowlist = allow_attr: rebuild_amendment: attrs: '' @@ -252,6 +263,59 @@ let ''; + remediate_problem = pkg: let + pkgName = lib.getName pkg; + + fullMessage = problem: (if problem.kind != problem.name then problem.kind + ": " else "") + problem.name + ": " + problem.message; + urlMessage = problem: let + urls = problem.urls or []; + in + lib.concatMapStrings (url: "\n - " + url) urls; + + printProblem = problem: "- " + fullMessage problem + urlMessage problem; + + problems = generateProblems pkg; + + ignorePattern = indentation: lib.pipe problems [ + (lib.filter ({handler, ...}: handler == "error")) + (builtins.map ({name, ...}: ''"${pkgName}"."${name}" = "warn";'')) + (lib.concatStringsSep ("\n" + indentation)) + ]; + in '' + + + Package problems: + + ${lib.pipe problems [ + (lib.filter ({handler, ...}: handler == "error")) + (builtins.map printProblem) + (lib.concatStringsSep "\n") + ]} + + You can use it anyway by ignoring its problems, using one of the + following methods: + + a) For `nixos-rebuild` you can add "warn" or "ignore" entries to + `nixpkgs.config.problems.handlers` inside configuration.nix, + like this: + + { + nixpkgs.config.problems.handlers = { + ${ignorePattern " "} + }; + } + + b) For `nix-env`, `nix-build`, `nix-shell` or any other Nix command you can add + "warn" or "ignore" to `problems.handlers` in + ~/.config/nixpkgs/config.nix, like this: + + { + problems.handlers = { + ${ignorePattern " "} + }; + } + ''; + remediateOutputsToInstall = attrs: let expectedOutputs = attrs.meta.outputsToInstall or []; actualOutputs = attrs.outputs or [ "out" ]; @@ -280,14 +344,24 @@ let else throw; in handler msg; - handleEvalWarning = { meta, attrs }: { reason , errormsg ? "" }: + handleEvalWarning = { meta, attrs }: { reason, errormsg ? "" }: let - remediationMsg = (builtins.getAttr reason remediation) attrs; - msg = if inHydra then "Warning while evaluating ${getNameWithVersion attrs}: «${reason}»: ${errormsg}" - else "Package ${getNameWithVersion attrs} in ${pos_str meta} ${errormsg}, continuing anyway." - + (optionalString (remediationMsg != "") "\n${remediationMsg}"); - isEnabled = findFirst (x: x == reason) null showWarnings; - in if isEnabled != null then builtins.trace msg true else true; + msg = if inHydra then + "Warning while evaluating ${getNameWithVersion attrs}: «${reason}»: ${errormsg}" + else + "Package ${getNameWithVersion attrs} in ${pos_str meta} ${errormsg}, continuing anyway."; + in + builtins.trace msg true; + + # Deep type-checking. Note that calling `type.check` is not enough: see `lib.mkOptionType`'s documentation. + # We don't include this in lib for now because this function is flawed: it accepts things like `mkIf true 42`. + typeCheck = type: value: let + merged = lib.mergeDefinitions [ ] type [ + { file = lib.unknownModule; inherit value; } + ]; + eval = builtins.tryEval (builtins.deepSeq merged.mergedValue null); + in eval.success; + metaTypes = let types = import ./meta-types.nix { inherit lib; }; @@ -328,6 +402,10 @@ let unfree = bool; unsupported = bool; insecure = bool; + # This is checked in more detail further down + problems = attrsOf (attrsOf any); + # TODO: refactor once something like Profpatsch's types-simple will land + # This is currently dead code due to https://github.com/NixOS/nix/issues/2532 tests = { name = "test"; verify = x: x == {} || ( # Accept {} for tests that are unsupported @@ -359,6 +437,14 @@ let badPlatforms = platforms; }; + # Type of a meta.problems.* value + problemTypes = with lib.types; { + # Only allow some problem kinds to be used here in `meta` + kind = enum problemKindsManual; + message = str; + urls = listOf str; + }; + checkMetaAttr = let # Map attrs directly to the verify function for performance metaTypes' = mapAttrs (_: t: t.verify) metaTypes; @@ -372,7 +458,64 @@ let }" ] else [ "key 'meta.${k}' is unrecognized; expected one of: \n [${concatMapStringsSep ", " (x: "'${x}'") (attrNames metaTypes)}]" ]; - checkMeta = meta: optionals config.checkMeta (concatMap (attr: checkMetaAttr attr meta.${attr}) (attrNames meta)); + + checkMeta = + meta: + # Basic attribute checks + lib.optionals config.checkMeta ( + lib.remove null ( + lib.mapAttrsToList (checkMetaAttr "meta" metaTypes) meta + ) + ) + + # Extended checks for problems, as they do not fit the module system + ++ lib.optionals (meta ? problems) ( + + # Check problem kinds are correct + lib.pipe meta.problems [ + (lib.mapAttrs (name: problem: { kind = name; } // problem)) + (lib.mapAttrsToList ( + name: problem: + lib.mapAttrsToList (checkMetaAttr "meta.problems.${name}" problemTypes) problem + )) + (lib.concatLists) + (lib.remove null) + ] + + # Check that problem has a message (required field) + ++ lib.pipe meta.problems [ + (lib.mapAttrsToList ( + name: problem: + if problem ? message then + null + else + "key 'meta.problems.${name}.message' is missing" + )) + (lib.remove null) + ] + + # Check that some problem kinds are unqiue + ++ lib.pipe meta.problems [ + (lib.mapAttrs (name: problem: { kind = name; } // problem)) + # Count the number of instances of each problem kind, + # returns an attrset mapping kind -> count + (lib.foldlAttrs + (count: name: problem: + count // { ${problem.kind} = (count.${problem.kind} or []) ++ [ name ]; } + ) + {} + ) + # We only care about those that must be unique + (builtins.intersectAttrs problemKindsUnique') + (lib.filterAttrs (_: names: builtins.length names > 1)) + (lib.mapAttrsToList ( + kind: names: + "keys [ ${ + concatMapStringsSep " " (name: "'meta.problems.${name}'") names + } ] all have the same problem kind, which is not allowed for kind '${kind}'" + )) + ] + ); checkOutputsToInstall = attrs: let expectedOutputs = attrs.meta.outputsToInstall or []; @@ -405,7 +548,6 @@ let in if res != [] then { valid = "no"; reason = "unknown-meta"; errormsg = "has an invalid meta attrset:${concatMapStrings (x: "\n - " + x) res}\n"; } - # --- Put checks that cannot be ignored here --- else if checkOutputsToInstall attrs then { valid = "no"; reason = "broken-outputs"; errormsg = "has invalid meta.outputsToInstall"; } @@ -434,11 +576,31 @@ let } else if !(hasAllowedInsecure attrs) then { valid = "no"; reason = "insecure"; errormsg = "is marked as insecure"; } - - # --- warnings --- - # Please also update the type in /pkgs/top-level/config.nix alongside this. - else if hasNoMaintainers attrs then - { valid = "warn"; reason = "maintainerless"; errormsg = "has no maintainers"; } + # --- RFC 127 problems --- + # Please also update the type in pkgs/top-level/config.nix alongside this. + else + let + problems = (generateProblems attrs); + hasUnignoredProblems = lib.any ({handler, ...}: handler != "ignore") problems; + hasErrorProblems = lib.any ({handler, ...}: handler == "error") problems; + in + if hasUnignoredProblems then + { + valid = if hasErrorProblems then "no" else "warn"; + reason = "problem"; + errormsg = + if hasErrorProblems then + "has some problem that must be acknowledged" + else + "has the following problems: [ ${ + lib.pipe problems [ + (builtins.map (lib.getAttr "name")) + (builtins.map (x: ''"${x}"'')) + (lib.concatStringsSep " ") + ] + } ]" + ; + } # ----- else validYes; diff --git a/pkgs/stdenv/generic/problems.nix b/pkgs/stdenv/generic/problems.nix new file mode 100644 index 000000000000000..fd2e67e12047c22 --- /dev/null +++ b/pkgs/stdenv/generic/problems.nix @@ -0,0 +1,102 @@ +# Implementation of RFC 127 + +{ lib }: + +rec { + # Problem handler levels + problemHandlers = [ "error" "warn" "ignore" ]; + + # Take the maximum (highest severity) problem handler + maxProblemHandler = + a: b: + if a == "error" || b == "error" then + "error" + else if a == "warn" || b == "warn" then + "warn" + else + "ignore" + ; + + # Currently known list of known problem kinds. Keep up to date with pkgs/top-level/config.nix + problemKinds = [ + "removal" + "deprecated" + "maintainerless" + "insecure" + "broken" + "unsupported" + ]; + + # Problem kinds that are currently allowed in `meta.problems` + problemKindsManual = [ + "removal" + "deprecated" + ]; + + # Problem kinds that are only allowed up to once per package + problemKindsUnique = [ + "removal" + "maintainerless" + ]; + # Same thing but a set with null values (comes in handy at times) + problemKindsUnique' = lib.pipe problemKindsUnique [ + (map (name: { inherit name; value = null; })) + lib.listToAttrs + ]; + + # Extract a list of problems from the derivation. Every problem has at least a `kind` and `message` field. + # generateProblems :: Derivation -> [{ name, kind, message, handler, … }] + generateProblems = + # Pass through helper functions from check-meta + { hasNoMaintainers }: + # config.problems + { handlers, matchers }: + pkg: + let + # Sometimes the name field of a derivation has some string context attached to it. + # This can happen for example if the name was generated from its src attribute, which is a derviation. + # In that case getName would fail with something like "the string 'run-test-trivial-overriding-bin-fail' is not allowed to refer to a store path (such as '!out!/nix/store/k9c8q6c11wsi0f8skafn3hj69rjynn7l-test-trivial-overriding-bin-fail.drv')" + # Using this is safe here because we only use the name for string comparisons, with no risk of it landing in a derivation. + pkgName = lib.getName (pkg // { name = builtins.unsafeDiscardStringContext pkg.name; }); + + # Take the problems plus add automatically generated ones + problems = + (pkg.meta.problems or {}) + // lib.optionalAttrs (hasNoMaintainers pkg) { + maintainerless = { + message = "This package has no declared maintainer, i.e. an empty `meta.maintainers` attribute"; + }; + }; + + # Inject default values, since metaTypes can't do it for us currently + addDefaults = name: problem: { inherit name; kind = name; } // problem; + + # Determine the handler level from config for this problem + addHandler = problem: + let + handler = + # Try to find an explicit handler + (handlers.${pkgName} or {}).${problem.name} + # Fall back, iterating through the matchers + or ( + lib.pipe matchers [ + # Find matches + (lib.filter + (matcher: + (if matcher.name != null then problem.name == matcher.name else true) + && (if matcher.kind != null then problem.kind == matcher.kind else true) + && (if matcher.package != null then pkgName == matcher.package else true) + ) + ) + # Extract handler level + (builtins.map (lib.getAttr "handler")) + # Take the strongest matched handler level + (lib.foldl' maxProblemHandler "ignore") + ] + ); + in + problem // { inherit handler; }; + in + builtins.map addHandler + (lib.mapAttrsToList addDefaults problems); +} diff --git a/pkgs/top-level/config.nix b/pkgs/top-level/config.nix index 67a9a60dbaeaa34..12442a4136d553c 100644 --- a/pkgs/top-level/config.nix +++ b/pkgs/top-level/config.nix @@ -13,6 +13,7 @@ let literalExpression mapAttrsToList mkOption + mkRemovedOptionModule optionals types ; @@ -36,11 +37,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 */ @@ -140,21 +148,6 @@ let feature = "build packages with ROCm support by default"; }; - showDerivationWarnings = mkOption { - type = types.listOf (types.enum [ "maintainerless" ]); - default = []; - description = '' - Which warnings to display for potentially dangerous - or deprecated values passed into `stdenv.mkDerivation`. - - A list of warnings can be found in - [/pkgs/stdenv/generic/check-meta.nix](https://github.com/NixOS/nixpkgs/blob/master/pkgs/stdenv/generic/check-meta.nix). - - This is not a stable interface; warnings may be added, changed - or removed without prior notice. - ''; - }; - checkMeta = mkOption { type = types.bool; default = false; @@ -162,6 +155,92 @@ let Whether to check that the `meta` attribute of derivations are correct during evaluation time. ''; }; + + problems = + let + inherit (import ../stdenv/generic/problems.nix { inherit lib; }) problemHandlers problemKinds; + handlerType = types.enum problemHandlers; + problemKindType = types.enum problemKinds; + in + { + handlers = 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 = mkOption { + type = types.listOf ( + types.submodule ({config, ...}: { + options = { + package = mkOption { + type = types.nullOr types.str; + description = "Match problems of packages with this name"; + default = null; + }; + name = mkOption { + type = types.nullOr types.str; + description = "Match problems with this problem name"; + default = null; + }; + kind = mkOption { + type = types.nullOr problemKindType; + description = "Match problems of this problem kind"; + default = null; + }; + handler = mkOption { + type = handlerType; + description = "Specify the handler for matched problems"; + }; + + # Temporary hack to get assertions in submodules, see global assertions below + assertions = mkOption { + type = types.listOf types.anything; + default = []; + internal = true; + }; + }; + config = { + assertions = [ + { + assertion = !(config.package != null && config.name != null); + message = ''A matcher cannot match on both a package and problem name as this would not be a wildcard, for that use `problems.handlers = { ${toString config.package}.${toString config.name} = "${toString config.handler}"; };` instead''; + } + ]; + }; + }) + ); + 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"; + } + ]; + }; + }; }; in { @@ -174,12 +253,39 @@ in { in r // { _undeclared = r; }; }; + imports = [ + (mkRemovedOptionModule ["showDerivationWarnings"] "See `config.problems.matchers` and the respective documentation. Be aware that the semantics have changed!") + ]; + + inherit options; config = { warnings = optionals config.warnUndeclaredOptions ( mapAttrsToList (k: v: "undeclared Nixpkgs option set: config.${k}") config._undeclared or {} ); + + 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 = "error"; + } + { + kind = "maintainerless"; + handler = "ignore"; + } + # Wildcard for everything else + { + handler = "ignore"; + } + ]; }; } diff --git a/pkgs/top-level/default.nix b/pkgs/top-level/default.nix index 5c224802d5bf334..20243b45a5fa36a 100644 --- a/pkgs/top-level/default.nix +++ b/pkgs/top-level/default.nix @@ -98,7 +98,15 @@ in let }; # take all the rest as-is - config = lib.showWarnings configEval.config.warnings configEval.config; + config = + let + failedAssertions = map (x: x.message) (lib.filter (x: !x.assertion) configEval.config.assertions); + in + if failedAssertions != [] + then throw "\nFailed assertions:\n${lib.concatStringsSep "\n" (map (x: "- ${x}") failedAssertions)}" + 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 From 2c52dcf4c0b0723bebc3832a1023cfd356fbb490 Mon Sep 17 00:00:00 2001 From: Silvan Mosberger Date: Mon, 22 Jul 2024 00:43:28 +0200 Subject: [PATCH 2/3] WIP problems testing Run with `nix-build -A tests.problems` --- pkgs/test/default.nix | 2 + .../problems/cases/removed-error/default.nix | 15 ++++++ .../cases/removed-error/expected-stderr | 32 +++++++++++ pkgs/test/problems/default.nix | 53 +++++++++++++++++++ 4 files changed, 102 insertions(+) create mode 100644 pkgs/test/problems/cases/removed-error/default.nix create mode 100644 pkgs/test/problems/cases/removed-error/expected-stderr create mode 100644 pkgs/test/problems/default.nix diff --git a/pkgs/test/default.nix b/pkgs/test/default.nix index 29125f1d2979f7f..012786731bcc348 100644 --- a/pkgs/test/default.nix +++ b/pkgs/test/default.nix @@ -143,6 +143,8 @@ with pkgs; overriding = callPackage ./overriding.nix { }; + problems = recurseIntoAttrs (callPackage ./problems { }); + texlive = callPackage ./texlive {}; cuda = callPackage ./cuda { }; diff --git a/pkgs/test/problems/cases/removed-error/default.nix b/pkgs/test/problems/cases/removed-error/default.nix new file mode 100644 index 000000000000000..43b62a3fcf51b05 --- /dev/null +++ b/pkgs/test/problems/cases/removed-error/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.problems = { + removal.message = "This package has been abandoned upstream and will be removed"; + }; +} diff --git a/pkgs/test/problems/cases/removed-error/expected-stderr b/pkgs/test/problems/cases/removed-error/expected-stderr new file mode 100644 index 000000000000000..915218a9ad02404 --- /dev/null +++ b/pkgs/test/problems/cases/removed-error/expected-stderr @@ -0,0 +1,32 @@ +error: Package ‘a-0’ in /nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-default.nix:11 has some problem that must be acknowledged, refusing to evaluate. + + + +Package problems: + +- removal: This package has been abandoned upstream and will be removed + +You can use it anyway by ignoring its problems, using one of the +following methods: + +a) For `nixos-rebuild` you can add "warn" or "ignore" entries to + `nixpkgs.config.problems.handlers` inside configuration.nix, + like this: + + { + nixpkgs.config.problems.handlers = { + "a"."removal" = "warn"; + }; + } + +b) For `nix-env`, `nix-build`, `nix-shell` or any other Nix command you can add + "warn" or "ignore" to `problems.handlers` in + ~/.config/nixpkgs/config.nix, like this: + + { + problems.handlers = { + "a"."removal" = "warn"; + }; + } + +(use '--show-trace' to show detailed location information) diff --git a/pkgs/test/problems/default.nix b/pkgs/test/problems/default.nix new file mode 100644 index 000000000000000..26fe640673d565b --- /dev/null +++ b/pkgs/test/problems/default.nix @@ -0,0 +1,53 @@ +{ + lib, + nixVersions, + runCommandNoCC, + removeReferencesTo, + path, +}: +let + nixpkgs = lib.cleanSource path; +in +lib.mapAttrs ( + name: _: + let + nixFile = "${./cases + "/${name}/default.nix"}"; + result = runCommandNoCC "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 nixVersions.minimum}/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[@]}" > >(tee $out/stdout) 2> >(tee $out/stderr) + set +e + echo "$?" > $out/code + echo "Command exited with code $(<$out/code)" + remove-references-to -t ${nixFile} $out/* + ''; + checker = runCommandNoCC "test-problems-check-${name}" { } '' + if ! diff ${result}/stderr ${./cases + "/${name}/expected-stderr"}; then + echo "Output of $(< ${result}/command) does not match what was expected (${result}/stderr)" + exit 1 + fi + touch $out + ''; + in + lib.nameValuePair name checker +) (builtins.readDir ./cases) From 69b558415905ef2468ae556f2318ee853d797bbb Mon Sep 17 00:00:00 2001 From: AkechiShiro <14914796+AkechiShiro@users.noreply.github.com> Date: Thu, 29 Aug 2024 22:49:17 +0200 Subject: [PATCH 3/3] RFC 127: Add implementation tests * Also fix merge conflicts * Make sure that performance PR is conserved * Use RunCommand instead of alias RunCommandNoCC * Add enum types to meta-types.nix and use them for problemTypes * Nixfmt (excepted check-meta.nix) * "import" elem * Remove dead code --- pkgs/stdenv/generic/check-meta.nix | 86 ++++++++----------- pkgs/stdenv/generic/meta-types.nix | 84 ++++++++++++------ pkgs/stdenv/generic/problems.nix | 53 +++++++----- .../default.nix | 23 +++++ .../expected-stderr | 38 ++++++++ .../deprecated-and-removal-error/default.nix | 22 +++++ .../expected-stderr | 35 ++++++++ .../cases/deprecated-error/default.nix | 20 +++++ .../cases/deprecated-error/expected-stderr | 32 +++++++ .../cases/deprecated-ignore/default.nix | 18 ++++ .../cases/deprecated-ignore/expected-stderr | 1 + .../cases/deprecated-warn/default.nix | 18 ++++ .../cases/deprecated-warn/expected-stderr | 2 + .../cases/invalid-kind-error/default.nix | 15 ++++ .../cases/invalid-kind-error/expected-stderr | 7 ++ .../default.nix | 23 +++++ .../expected-stderr | 2 + .../default.nix | 19 ++++ .../expected-stderr | 2 + .../cases/maintainerless-error/default.nix | 17 ++++ .../maintainerless-error/expected-stderr | 32 +++++++ .../cases/maintainerless-ignore/default.nix | 17 ++++ .../maintainerless-ignore/expected-stderr | 1 + .../cases/maintainerless-warn/default.nix | 17 ++++ .../cases/maintainerless-warn/expected-stderr | 2 + .../problems/cases/no-problems/default.nix | 15 ++++ .../cases/no-problems/expected-stderr | 1 + .../problems/cases/removal-error/default.nix | 15 ++++ .../cases/removal-error/expected-stderr | 32 +++++++ .../problems/cases/removal-ignore/default.nix | 17 ++++ .../cases/removal-ignore/expected-stderr | 1 + .../cases/removal-twice-error/default.nix | 21 +++++ .../cases/removal-twice-error/expected-stderr | 6 ++ .../problems/cases/removal-warn/default.nix | 18 ++++ .../cases/removal-warn/expected-stderr | 2 + pkgs/test/problems/default.nix | 10 +-- 36 files changed, 616 insertions(+), 108 deletions(-) create mode 100644 pkgs/test/problems/cases/deprecated-and-removal-and-maintainerless-error/default.nix create mode 100644 pkgs/test/problems/cases/deprecated-and-removal-and-maintainerless-error/expected-stderr create mode 100644 pkgs/test/problems/cases/deprecated-and-removal-error/default.nix create mode 100644 pkgs/test/problems/cases/deprecated-and-removal-error/expected-stderr create mode 100644 pkgs/test/problems/cases/deprecated-error/default.nix create mode 100644 pkgs/test/problems/cases/deprecated-error/expected-stderr create mode 100644 pkgs/test/problems/cases/deprecated-ignore/default.nix create mode 100644 pkgs/test/problems/cases/deprecated-ignore/expected-stderr create mode 100644 pkgs/test/problems/cases/deprecated-warn/default.nix create mode 100644 pkgs/test/problems/cases/deprecated-warn/expected-stderr create mode 100644 pkgs/test/problems/cases/invalid-kind-error/default.nix create mode 100644 pkgs/test/problems/cases/invalid-kind-error/expected-stderr create mode 100644 pkgs/test/problems/cases/maintainerless-and-removal-and-deprecated-warn/default.nix create mode 100644 pkgs/test/problems/cases/maintainerless-and-removal-and-deprecated-warn/expected-stderr create mode 100644 pkgs/test/problems/cases/maintainerless-and-removal-warn/default.nix create mode 100644 pkgs/test/problems/cases/maintainerless-and-removal-warn/expected-stderr create mode 100644 pkgs/test/problems/cases/maintainerless-error/default.nix create mode 100644 pkgs/test/problems/cases/maintainerless-error/expected-stderr create mode 100644 pkgs/test/problems/cases/maintainerless-ignore/default.nix create mode 100644 pkgs/test/problems/cases/maintainerless-ignore/expected-stderr create mode 100644 pkgs/test/problems/cases/maintainerless-warn/default.nix create mode 100644 pkgs/test/problems/cases/maintainerless-warn/expected-stderr create mode 100644 pkgs/test/problems/cases/no-problems/default.nix create mode 100644 pkgs/test/problems/cases/no-problems/expected-stderr create mode 100644 pkgs/test/problems/cases/removal-error/default.nix create mode 100644 pkgs/test/problems/cases/removal-error/expected-stderr create mode 100644 pkgs/test/problems/cases/removal-ignore/default.nix create mode 100644 pkgs/test/problems/cases/removal-ignore/expected-stderr create mode 100644 pkgs/test/problems/cases/removal-twice-error/default.nix create mode 100644 pkgs/test/problems/cases/removal-twice-error/expected-stderr create mode 100644 pkgs/test/problems/cases/removal-warn/default.nix create mode 100644 pkgs/test/problems/cases/removal-warn/expected-stderr diff --git a/pkgs/stdenv/generic/check-meta.nix b/pkgs/stdenv/generic/check-meta.nix index 12012043a0d7969..537380724d3825e 100644 --- a/pkgs/stdenv/generic/check-meta.nix +++ b/pkgs/stdenv/generic/check-meta.nix @@ -353,19 +353,9 @@ let in builtins.trace msg true; - # Deep type-checking. Note that calling `type.check` is not enough: see `lib.mkOptionType`'s documentation. - # We don't include this in lib for now because this function is flawed: it accepts things like `mkIf true 42`. - typeCheck = type: value: let - merged = lib.mergeDefinitions [ ] type [ - { file = lib.unknownModule; inherit value; } - ]; - eval = builtins.tryEval (builtins.deepSeq merged.mergedValue null); - in eval.success; - - metaTypes = let types = import ./meta-types.nix { inherit lib; }; - inherit (types) str union int attrs attrsOf any listOf bool; + inherit (types) str union int attrs attrsOf any listOf bool enum; platforms = listOf (union [ str (attrsOf any) ]); # see lib.meta.platformMatch in { # These keys are documented @@ -404,8 +394,11 @@ let insecure = bool; # This is checked in more detail further down problems = attrsOf (attrsOf any); - # TODO: refactor once something like Profpatsch's types-simple will land - # This is currently dead code due to https://github.com/NixOS/nix/issues/2532 + problemTypes = { + kind = enum problemKindsManual; + message = str; + urls = listOf str; + }; tests = { name = "test"; verify = x: x == {} || ( # Accept {} for tests that are unsupported @@ -437,15 +430,8 @@ let badPlatforms = platforms; }; - # Type of a meta.problems.* value - problemTypes = with lib.types; { - # Only allow some problem kinds to be used here in `meta` - kind = enum problemKindsManual; - message = str; - urls = listOf str; - }; - - checkMetaAttr = let + # Check that a value matches a specific type. Returns an error message, or null if the check passed + checkMetaAttr = let # Map attrs directly to the verify function for performance metaTypes' = mapAttrs (_: t: t.verify) metaTypes; in k: v: @@ -458,15 +444,7 @@ let }" ] else [ "key 'meta.${k}' is unrecognized; expected one of: \n [${concatMapStringsSep ", " (x: "'${x}'") (attrNames metaTypes)}]" ]; - - checkMeta = - meta: - # Basic attribute checks - lib.optionals config.checkMeta ( - lib.remove null ( - lib.mapAttrsToList (checkMetaAttr "meta" metaTypes) meta - ) - ) + checkMeta = meta: optionals config.checkMeta (concatMap (attr: checkMetaAttr attr meta.${attr}) (attrNames meta) # Extended checks for problems, as they do not fit the module system ++ lib.optionals (meta ? problems) ( @@ -476,7 +454,7 @@ let (lib.mapAttrs (name: problem: { kind = name; } // problem)) (lib.mapAttrsToList ( name: problem: - lib.mapAttrsToList (checkMetaAttr "meta.problems.${name}" problemTypes) problem + lib.mapAttrsToList (checkMetaAttr "meta.problems.${name}" meta.problemTypes) problem )) (lib.concatLists) (lib.remove null) @@ -511,11 +489,11 @@ let (lib.mapAttrsToList ( kind: names: "keys [ ${ - concatMapStringsSep " " (name: "'meta.problems.${name}'") names + lib.concatMapStringsSep " " (name: "'meta.problems.${name}'") names } ] all have the same problem kind, which is not allowed for kind '${kind}'" )) ] - ); + )); checkOutputsToInstall = attrs: let expectedOutputs = attrs.meta.outputsToInstall or []; @@ -533,21 +511,24 @@ let # reason is one of "unfree", "blocklisted", "broken", "insecure", ... # !!! reason strings are hardcoded into OfBorg, make sure to keep them in sync # Along with a boolean flag for each reason - checkValidity = - let - validYes = { - valid = "yes"; - handled = true; - }; - in - attrs: + checkValidity = attrs: # Check meta attribute types first, to make sure it is always called even when there are other issues # Note that this is not a full type check and functions below still need to by careful about their inputs! - let - res = checkMeta (attrs.meta or {}); - in - if res != [] then - { valid = "no"; reason = "unknown-meta"; errormsg = "has an invalid meta attrset:${concatMapStrings (x: "\n - " + x) res}\n"; } + let res = checkMeta (attrs.meta or {}); in if res != [] then + { valid = "no"; reason = "unknown-meta"; errormsg = "has an invalid meta attrset:${lib.concatMapStrings (x: "\n - " + x) res}\n"; + unfree = false; nonSource = false; broken = false; unsupported = false; insecure = false; + } + else { + unfree = hasUnfreeLicense attrs; + nonSource = hasNonSourceProvenance attrs; + broken = isMarkedBroken attrs; + unsupported = hasUnsupportedPlatform attrs; + insecure = isMarkedInsecure attrs; + } // ( + # Check meta attribute types first, to make sure it is always called even when there are other issues + # Note that this is not a full type check and functions below still need to by careful about their inputs! + let res = checkMeta (attrs.meta or {}); in if res != [] then + { valid = "no"; reason = "unknown-meta"; errormsg = "has malformed metadata:${lib.concatMapStrings (x: "\n\t - " + x) res}"; } # --- Put checks that cannot be ignored here --- else if checkOutputsToInstall attrs then { valid = "no"; reason = "broken-outputs"; errormsg = "has invalid meta.outputsToInstall"; } @@ -559,10 +540,12 @@ let { valid = "no"; reason = "blocklisted"; errormsg = "has a blocklisted license (‘${showLicense attrs.meta.license}’)"; } else if hasDeniedNonSourceProvenance attrs then { valid = "no"; reason = "non-source"; errormsg = "contains elements not built from source (‘${showSourceType attrs.meta.sourceProvenance}’)"; } + + # --- Put checks that can be ignored here --- else if !allowBroken && attrs.meta.broken or false then { valid = "no"; reason = "broken"; errormsg = "is marked as broken"; } else if !allowUnsupportedSystem && hasUnsupportedPlatform attrs then - let toPretty' = toPretty { + let toPretty = lib.generators.toPretty { allowPrettyValues = true; indent = " "; }; @@ -570,8 +553,8 @@ let errormsg = '' is not available on the requested hostPlatform: hostPlatform.config = "${hostPlatform.config}" - package.meta.platforms = ${toPretty' (attrs.meta.platforms or [])} - package.meta.badPlatforms = ${toPretty' (attrs.meta.badPlatforms or [])} + package.meta.platforms = ${toPretty (attrs.meta.platforms or [])} + package.meta.badPlatforms = ${toPretty (attrs.meta.badPlatforms or [])} ''; } else if !(hasAllowedInsecure attrs) then @@ -602,8 +585,7 @@ let ; } # ----- - else validYes; - + else { valid = "yes"; }); # The meta attribute is passed in the resulting attribute set, # but it's not part of the actual derivation, i.e., it's not diff --git a/pkgs/stdenv/generic/meta-types.nix b/pkgs/stdenv/generic/meta-types.nix index ddbd1daca696ea6..d944f5dd2b32d19 100644 --- a/pkgs/stdenv/generic/meta-types.nix +++ b/pkgs/stdenv/generic/meta-types.nix @@ -5,7 +5,20 @@ # TODO: add a method to the module system types # see https://github.com/NixOS/nixpkgs/pull/273935#issuecomment-1854173100 let - inherit (builtins) isString isInt isAttrs isList all any attrValues isFunction isBool concatStringsSep isFloat; + inherit (builtins) + isString + isInt + isAttrs + isList + all + any + attrValues + isFunction + isBool + concatStringsSep + isFloat + elem + ; isTypeDef = t: isAttrs t && t ? name && isString t.name && t ? verify && isFunction t.verify; in @@ -14,7 +27,7 @@ lib.fix (self: { name = "string"; verify = isString; }; - str = self.string; # Type alias + str = self.string; # Type alias any = { name = "any"; @@ -46,31 +59,48 @@ lib.fix (self: { verify = isList; }; - attrsOf = t: assert isTypeDef t; let - inherit (t) verify; - in { - name = "attrsOf<${t.name}>"; - verify = - # attrsOf can be optimised to just isAttrs - if t == self.any then isAttrs - else attrs: isAttrs attrs && all verify (attrValues attrs); - }; + attrsOf = + t: + assert isTypeDef t; + let + inherit (t) verify; + in + { + name = "attrsOf<${t.name}>"; + verify = + # attrsOf can be optimised to just isAttrs + if t == self.any then isAttrs else attrs: isAttrs attrs && all verify (attrValues attrs); + }; - listOf = t: assert isTypeDef t; let - inherit (t) verify; - in { - name = "listOf<${t.name}>"; - verify = - # listOf can be optimised to just isList - if t == self.any then isList - else v: isList v && all verify v; - }; + listOf = + t: + assert isTypeDef t; + let + inherit (t) verify; + in + { + name = "listOf<${t.name}>"; + verify = + # listOf can be optimised to just isList + if t == self.any then isList else v: isList v && all verify v; + }; - union = types: assert all isTypeDef types; let - # Store a list of functions so we don't have to pay the cost of attrset lookups at runtime. - funcs = map (t: t.verify) types; - in { - name = "union<${concatStringsSep "," (map (t: t.name) types)}>"; - verify = v: any (func: func v) funcs; - }; + union = + types: + assert all isTypeDef types; + let + # Store a list of functions so we don't have to pay the cost of attrset lookups at runtime. + funcs = map (t: t.verify) types; + in + { + name = "union<${concatStringsSep "," (map (t: t.name) types)}>"; + verify = v: any (func: func v) funcs; + }; + enum = + values: + assert isList values && all isString values; + { + name = "enum<${concatStringsSep "," values}>"; + verify = v: isString v && elem v values; + }; }) diff --git a/pkgs/stdenv/generic/problems.nix b/pkgs/stdenv/generic/problems.nix index fd2e67e12047c22..ff5f32d92a93131 100644 --- a/pkgs/stdenv/generic/problems.nix +++ b/pkgs/stdenv/generic/problems.nix @@ -4,7 +4,11 @@ rec { # Problem handler levels - problemHandlers = [ "error" "warn" "ignore" ]; + problemHandlers = [ + "error" + "warn" + "ignore" + ]; # Take the maximum (highest severity) problem handler maxProblemHandler = @@ -14,8 +18,7 @@ rec { else if a == "warn" || b == "warn" then "warn" else - "ignore" - ; + "ignore"; # Currently known list of known problem kinds. Keep up to date with pkgs/top-level/config.nix problemKinds = [ @@ -40,7 +43,10 @@ rec { ]; # Same thing but a set with null values (comes in handy at times) problemKindsUnique' = lib.pipe problemKindsUnique [ - (map (name: { inherit name; value = null; })) + (map (name: { + inherit name; + value = null; + })) lib.listToAttrs ]; @@ -61,7 +67,7 @@ rec { # Take the problems plus add automatically generated ones problems = - (pkg.meta.problems or {}) + (pkg.meta.problems or { }) // lib.optionalAttrs (hasNoMaintainers pkg) { maintainerless = { message = "This package has no declared maintainer, i.e. an empty `meta.maintainers` attribute"; @@ -69,34 +75,37 @@ rec { }; # Inject default values, since metaTypes can't do it for us currently - addDefaults = name: problem: { inherit name; kind = name; } // problem; + addDefaults = + name: problem: + { + inherit name; + kind = name; + } + // problem; # Determine the handler level from config for this problem - addHandler = problem: + addHandler = + problem: let handler = # Try to find an explicit handler - (handlers.${pkgName} or {}).${problem.name} - # Fall back, iterating through the matchers - or ( - lib.pipe matchers [ + (handlers.${pkgName} or { }).${problem.name} + # Fall back, iterating through the matchers + or (lib.pipe matchers [ # Find matches - (lib.filter - (matcher: - (if matcher.name != null then problem.name == matcher.name else true) - && (if matcher.kind != null then problem.kind == matcher.kind else true) - && (if matcher.package != null then pkgName == matcher.package else true) - ) - ) + (lib.filter ( + matcher: + (if matcher.name != null then problem.name == matcher.name else true) + && (if matcher.kind != null then problem.kind == matcher.kind else true) + && (if matcher.package != null then pkgName == matcher.package else true) + )) # Extract handler level (builtins.map (lib.getAttr "handler")) # Take the strongest matched handler level (lib.foldl' maxProblemHandler "ignore") - ] - ); + ]); in problem // { inherit handler; }; in - builtins.map addHandler - (lib.mapAttrsToList addDefaults problems); + builtins.map addHandler (lib.mapAttrsToList addDefaults problems); } 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 000000000000000..562c7594c5d170d --- /dev/null +++ b/pkgs/test/problems/cases/deprecated-and-removal-and-maintainerless-error/default.nix @@ -0,0 +1,23 @@ +{ 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.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 000000000000000..7b72e3fc5052d97 --- /dev/null +++ b/pkgs/test/problems/cases/deprecated-and-removal-and-maintainerless-error/expected-stderr @@ -0,0 +1,38 @@ +error: Package ‘a-0’ in /nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-default.nix:17 has some problem that must be acknowledged, refusing to evaluate. + + + +Package problems: + +- deprecated: Package is deprecated and replaced by b +- maintainerless: This package has no declared maintainer, i.e. an empty `meta.maintainers` attribute +- removal: Package will be removed. + +You can use it anyway by ignoring its problems, using one of the +following methods: + +a) For `nixos-rebuild` you can add "warn" or "ignore" entries to + `nixpkgs.config.problems.handlers` inside configuration.nix, + like this: + + { + nixpkgs.config.problems.handlers = { + "a"."deprecated" = "warn"; + "a"."maintainerless" = "warn"; + "a"."removal" = "warn"; + }; + } + +b) For `nix-env`, `nix-build`, `nix-shell` or any other Nix command you can add + "warn" or "ignore" to `problems.handlers` in + ~/.config/nixpkgs/config.nix, like this: + + { + problems.handlers = { + "a"."deprecated" = "warn"; + "a"."maintainerless" = "warn"; + "a"."removal" = "warn"; + }; + } + +(use '--show-trace' to show detailed location information) 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 000000000000000..20a18cc17c78e04 --- /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 000000000000000..f5a0f1cce688de6 --- /dev/null +++ b/pkgs/test/problems/cases/deprecated-and-removal-error/expected-stderr @@ -0,0 +1,35 @@ +error: Package ‘a-0’ in /nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-default.nix:16 has some problem that must be acknowledged, refusing to evaluate. + + + +Package problems: + +- deprecated: Package is deprecated and replaced by b +- removal: Package will be removed + +You can use it anyway by ignoring its problems, using one of the +following methods: + +a) For `nixos-rebuild` you can add "warn" or "ignore" entries to + `nixpkgs.config.problems.handlers` inside configuration.nix, + like this: + + { + nixpkgs.config.problems.handlers = { + "a"."deprecated" = "warn"; + "a"."removal" = "warn"; + }; + } + +b) For `nix-env`, `nix-build`, `nix-shell` or any other Nix command you can add + "warn" or "ignore" to `problems.handlers` in + ~/.config/nixpkgs/config.nix, like this: + + { + problems.handlers = { + "a"."deprecated" = "warn"; + "a"."removal" = "warn"; + }; + } + +(use '--show-trace' to show detailed location information) 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 000000000000000..405d394bd893f2a --- /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 000000000000000..13fdb8d1706ba17 --- /dev/null +++ b/pkgs/test/problems/cases/deprecated-error/expected-stderr @@ -0,0 +1,32 @@ +error: Package ‘a-0’ in /nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-default.nix:15 has some problem that must be acknowledged, refusing to evaluate. + + + +Package problems: + +- deprecated: Package is deprecated and replaced by b + +You can use it anyway by ignoring its problems, using one of the +following methods: + +a) For `nixos-rebuild` you can add "warn" or "ignore" entries to + `nixpkgs.config.problems.handlers` inside configuration.nix, + like this: + + { + nixpkgs.config.problems.handlers = { + "a"."deprecated" = "warn"; + }; + } + +b) For `nix-env`, `nix-build`, `nix-shell` or any other Nix command you can add + "warn" or "ignore" to `problems.handlers` in + ~/.config/nixpkgs/config.nix, like this: + + { + problems.handlers = { + "a"."deprecated" = "warn"; + }; + } + +(use '--show-trace' to show detailed location information) 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 000000000000000..0ce36d294842314 --- /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 000000000000000..c255c1d5a32bd74 --- /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 000000000000000..bcafc3e96f6a7f0 --- /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 000000000000000..a55882a849c090f --- /dev/null +++ b/pkgs/test/problems/cases/deprecated-warn/expected-stderr @@ -0,0 +1,2 @@ +trace: Package a-0 in /nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-default.nix:15 has the following problems: [ "deprecated" ], continuing anyway. +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 000000000000000..a97437752acc1b0 --- /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 = { }; + }; +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 000000000000000..d27676672b6aca4 --- /dev/null +++ b/pkgs/test/problems/cases/invalid-kind-error/expected-stderr @@ -0,0 +1,7 @@ +error: Package ‘a-0’ in /nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-default.nix:11 has an invalid meta attrset: + - key 'meta.problems.invalid.kind' has invalid value; expected one of "removal", "deprecated", got + "invalid" +, refusing to evaluate. + + +(use '--show-trace' to show detailed location information) 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 000000000000000..9261c2e45fae9f4 --- /dev/null +++ b/pkgs/test/problems/cases/maintainerless-and-removal-and-deprecated-warn/default.nix @@ -0,0 +1,23 @@ +{ 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.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 000000000000000..656f92bc94fca19 --- /dev/null +++ b/pkgs/test/problems/cases/maintainerless-and-removal-and-deprecated-warn/expected-stderr @@ -0,0 +1,2 @@ +trace: Package a-0 in /nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-default.nix:17 has the following problems: [ "deprecated" "maintainerless" "removal" ], continuing anyway. +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 000000000000000..82266eafa95c6ad --- /dev/null +++ b/pkgs/test/problems/cases/maintainerless-and-removal-warn/default.nix @@ -0,0 +1,19 @@ +{ 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.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 000000000000000..1f8e51654aff6b8 --- /dev/null +++ b/pkgs/test/problems/cases/maintainerless-and-removal-warn/expected-stderr @@ -0,0 +1,2 @@ +trace: Package a-0 in /nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-default.nix:16 has the following problems: [ "maintainerless" "removal" ], continuing anyway. +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 000000000000000..150e623a4bdff62 --- /dev/null +++ b/pkgs/test/problems/cases/maintainerless-error/default.nix @@ -0,0 +1,17 @@ +{ 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.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 000000000000000..176fd604057ba68 --- /dev/null +++ b/pkgs/test/problems/cases/maintainerless-error/expected-stderr @@ -0,0 +1,32 @@ +error: Package ‘a-0’ in /nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-default.nix:15 has some problem that must be acknowledged, refusing to evaluate. + + + +Package problems: + +- maintainerless: This package has no declared maintainer, i.e. an empty `meta.maintainers` attribute + +You can use it anyway by ignoring its problems, using one of the +following methods: + +a) For `nixos-rebuild` you can add "warn" or "ignore" entries to + `nixpkgs.config.problems.handlers` inside configuration.nix, + like this: + + { + nixpkgs.config.problems.handlers = { + "a"."maintainerless" = "warn"; + }; + } + +b) For `nix-env`, `nix-build`, `nix-shell` or any other Nix command you can add + "warn" or "ignore" to `problems.handlers` in + ~/.config/nixpkgs/config.nix, like this: + + { + problems.handlers = { + "a"."maintainerless" = "warn"; + }; + } + +(use '--show-trace' to show detailed location information) 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 000000000000000..8531510073d4069 --- /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 000000000000000..c255c1d5a32bd74 --- /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 000000000000000..75708dcd42ad5cc --- /dev/null +++ b/pkgs/test/problems/cases/maintainerless-warn/default.nix @@ -0,0 +1,17 @@ +{ 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.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 000000000000000..45fd822f0feb5c8 --- /dev/null +++ b/pkgs/test/problems/cases/maintainerless-warn/expected-stderr @@ -0,0 +1,2 @@ +trace: Package a-0 in /nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-default.nix:15 has the following problems: [ "maintainerless" ], continuing anyway. +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 000000000000000..f7b14483233d99f --- /dev/null +++ b/pkgs/test/problems/cases/no-problems/default.nix @@ -0,0 +1,15 @@ +{ nixpkgs }: +let + pkgs = import nixpkgs { + system = "x86_64-linux"; + overlays = [ ]; + config = { + problems.handlers = { }; + }; + }; +in +pkgs.stdenvNoCC.mkDerivation { + pname = "a"; + version = "0"; + meta.maintainers = [ "hello" ]; +} 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 000000000000000..c255c1d5a32bd74 --- /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/removal-error/default.nix b/pkgs/test/problems/cases/removal-error/default.nix new file mode 100644 index 000000000000000..43b62a3fcf51b05 --- /dev/null +++ b/pkgs/test/problems/cases/removal-error/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.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 000000000000000..915218a9ad02404 --- /dev/null +++ b/pkgs/test/problems/cases/removal-error/expected-stderr @@ -0,0 +1,32 @@ +error: Package ‘a-0’ in /nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-default.nix:11 has some problem that must be acknowledged, refusing to evaluate. + + + +Package problems: + +- removal: This package has been abandoned upstream and will be removed + +You can use it anyway by ignoring its problems, using one of the +following methods: + +a) For `nixos-rebuild` you can add "warn" or "ignore" entries to + `nixpkgs.config.problems.handlers` inside configuration.nix, + like this: + + { + nixpkgs.config.problems.handlers = { + "a"."removal" = "warn"; + }; + } + +b) For `nix-env`, `nix-build`, `nix-shell` or any other Nix command you can add + "warn" or "ignore" to `problems.handlers` in + ~/.config/nixpkgs/config.nix, like this: + + { + problems.handlers = { + "a"."removal" = "warn"; + }; + } + +(use '--show-trace' to show detailed location information) 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 000000000000000..0cccb8873b276cb --- /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 000000000000000..c255c1d5a32bd74 --- /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 000000000000000..c2dec5f6d7cf7f2 --- /dev/null +++ b/pkgs/test/problems/cases/removal-twice-error/default.nix @@ -0,0 +1,21 @@ +{ nixpkgs }: +let + pkgs = import nixpkgs { + system = "x86_64-linux"; + overlays = [ ]; + config = { }; + }; +in +pkgs.stdenvNoCC.mkDerivation { + pname = "a"; + version = "0"; + 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 000000000000000..63b7c9eecfad1fd --- /dev/null +++ b/pkgs/test/problems/cases/removal-twice-error/expected-stderr @@ -0,0 +1,6 @@ +error: Package ‘a-0’ in /nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-default.nix:11 has an invalid meta attrset: + - keys [ 'meta.problems.removal' 'meta.problems.removal2' ] all have the same problem kind, which is not allowed for kind 'removal' +, refusing to evaluate. + + +(use '--show-trace' to show detailed location information) 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 000000000000000..0044c2fc39e0928 --- /dev/null +++ b/pkgs/test/problems/cases/removal-warn/default.nix @@ -0,0 +1,18 @@ +{ nixpkgs }: +let + pkgs = import nixpkgs { + system = "x86_64-linux"; + overlays = [ ]; + config = { + problems.handlers = { + "a"."removal" = "warn"; + }; + }; + }; +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 000000000000000..93f814163896c56 --- /dev/null +++ b/pkgs/test/problems/cases/removal-warn/expected-stderr @@ -0,0 +1,2 @@ +trace: Package a-0 in /nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-default.nix:15 has the following problems: [ "removal" ], continuing anyway. +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 index 26fe640673d565b..3366c3688eda8f2 100644 --- a/pkgs/test/problems/default.nix +++ b/pkgs/test/problems/default.nix @@ -1,7 +1,7 @@ { lib, nixVersions, - runCommandNoCC, + runCommand, removeReferencesTo, path, }: @@ -12,11 +12,7 @@ lib.mapAttrs ( name: _: let nixFile = "${./cases + "/${name}/default.nix"}"; - result = runCommandNoCC "test-problems-${name}" { - nativeBuildInputs = [ - removeReferencesTo - ]; - } '' + result = runCommand "test-problems-${name}" { nativeBuildInputs = [ removeReferencesTo ]; } '' export NIX_STATE_DIR=$(mktemp -d) mkdir $out @@ -41,7 +37,7 @@ lib.mapAttrs ( echo "Command exited with code $(<$out/code)" remove-references-to -t ${nixFile} $out/* ''; - checker = runCommandNoCC "test-problems-check-${name}" { } '' + checker = runCommand "test-problems-check-${name}" { } '' if ! diff ${result}/stderr ${./cases + "/${name}/expected-stderr"}; then echo "Output of $(< ${result}/command) does not match what was expected (${result}/stderr)" exit 1