meta.problems: Custom package problems [RFC127] (#478539)
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -629,6 +629,9 @@
|
||||
"chap-stdenv": [
|
||||
"index.html#chap-stdenv"
|
||||
],
|
||||
"sec-problems": [
|
||||
"index.html#sec-problems"
|
||||
],
|
||||
"sec-using-llvm": [
|
||||
"index.html#sec-using-llvm"
|
||||
],
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 <nixpkgs> { 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
|
||||
@@ -317,7 +303,7 @@ let
|
||||
${concatStrings (map (output: " - ${output}\n") missingOutputs)}
|
||||
'';
|
||||
|
||||
metaTypes =
|
||||
metaType =
|
||||
let
|
||||
types = import ./meta-types.nix { inherit lib; };
|
||||
inherit (types)
|
||||
@@ -329,13 +315,14 @@ let
|
||||
any
|
||||
listOf
|
||||
bool
|
||||
record
|
||||
;
|
||||
platforms = listOf (union [
|
||||
str
|
||||
(attrsOf any)
|
||||
]); # see lib.meta.platformMatch
|
||||
in
|
||||
{
|
||||
record {
|
||||
# These keys are documented
|
||||
description = str;
|
||||
mainProgram = str;
|
||||
@@ -374,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;
|
||||
@@ -404,34 +393,7 @@ let
|
||||
identifiers = attrs;
|
||||
};
|
||||
|
||||
# Map attrs directly to the verify function for performance
|
||||
metaTypes' = mapAttrs (_: t: t.verify) metaTypes;
|
||||
|
||||
checkMetaAttr =
|
||||
k: v:
|
||||
if metaTypes ? ${k} then
|
||||
if metaTypes'.${k} v then
|
||||
[ ]
|
||||
else
|
||||
[
|
||||
"key 'meta.${k}' has invalid value; expected ${metaTypes.${k}.name}, got\n ${
|
||||
toPretty { indent = " "; } v
|
||||
}"
|
||||
]
|
||||
else
|
||||
[
|
||||
"key 'meta.${k}' is unrecognized; expected one of: \n [${
|
||||
concatMapStringsSep ", " (x: "'${x}'") (attrNames metaTypes)
|
||||
}]"
|
||||
];
|
||||
|
||||
checkMeta = meta: concatMap (attr: checkMetaAttr attr meta.${attr}) (attrNames meta);
|
||||
|
||||
metaInvalid =
|
||||
if config.checkMeta then
|
||||
meta: !all (attr: metaTypes ? ${attr} && metaTypes'.${attr} meta.${attr}) (attrNames meta)
|
||||
else
|
||||
meta: false;
|
||||
metaInvalid = if config.checkMeta then meta: !metaType.verify meta else meta: false;
|
||||
|
||||
checkOutputsToInstall =
|
||||
if config.checkMeta then
|
||||
@@ -447,7 +409,7 @@ let
|
||||
# e.g brokenness or license.
|
||||
#
|
||||
# Return { valid: "yes", "warn" or "no" } and additionally
|
||||
# { reason: String; errormsg: String, remediation: String } if it is not valid, where
|
||||
# { reason: String; msg: String, remediation: String } if it is not valid, where
|
||||
# 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
|
||||
@@ -458,8 +420,8 @@ let
|
||||
if metaInvalid (attrs.meta or { }) then
|
||||
{
|
||||
reason = "unknown-meta";
|
||||
errormsg = "has an invalid meta attrset:${
|
||||
concatMapStrings (x: "\n - " + x) (checkMeta attrs.meta)
|
||||
msg = "has an invalid meta attrset:${
|
||||
concatMapStrings (x: "\n - " + x) (metaType.errors "${getName attrs}.meta" attrs.meta)
|
||||
}\n";
|
||||
remediation = "";
|
||||
}
|
||||
@@ -468,7 +430,7 @@ let
|
||||
else if checkOutputsToInstall attrs then
|
||||
{
|
||||
reason = "broken-outputs";
|
||||
errormsg = "has invalid meta.outputsToInstall";
|
||||
msg = "has invalid meta.outputsToInstall";
|
||||
remediation = remediateOutputsToInstall attrs;
|
||||
}
|
||||
|
||||
@@ -476,25 +438,25 @@ let
|
||||
else if hasDeniedUnfreeLicense attrs && !(hasAllowlistedLicense attrs) then
|
||||
{
|
||||
reason = "unfree";
|
||||
errormsg = "has an unfree license (‘${showLicense attrs.meta.license}’)";
|
||||
msg = "has an unfree license (‘${showLicense attrs.meta.license}’)";
|
||||
remediation = remediate_allowlist "Unfree" (remediate_predicate "allowUnfreePredicate" attrs);
|
||||
}
|
||||
else if hasBlocklistedLicense attrs then
|
||||
{
|
||||
reason = "blocklisted";
|
||||
errormsg = "has a blocklisted license (‘${showLicense attrs.meta.license}’)";
|
||||
msg = "has a blocklisted license (‘${showLicense attrs.meta.license}’)";
|
||||
remediation = "";
|
||||
}
|
||||
else if hasDeniedNonSourceProvenance attrs then
|
||||
{
|
||||
reason = "non-source";
|
||||
errormsg = "contains elements not built from source (‘${showSourceType attrs.meta.sourceProvenance}’)";
|
||||
msg = "contains elements not built from source (‘${showSourceType attrs.meta.sourceProvenance}’)";
|
||||
remediation = remediate_allowlist "NonSource" (remediate_predicate "allowNonSourcePredicate" attrs);
|
||||
}
|
||||
else if hasDeniedBroken attrs then
|
||||
{
|
||||
reason = "broken";
|
||||
errormsg = "is marked as broken";
|
||||
msg = "is marked as broken";
|
||||
remediation = remediate_allowlist "Broken" "";
|
||||
}
|
||||
else if hasUnsupportedPlatform attrs && !allowUnsupportedSystem then
|
||||
@@ -506,7 +468,7 @@ let
|
||||
in
|
||||
{
|
||||
reason = "unsupported";
|
||||
errormsg = ''
|
||||
msg = ''
|
||||
is not available on the requested hostPlatform:
|
||||
hostPlatform.system = "${hostPlatform.system}"
|
||||
package.meta.platforms = ${toPretty' (attrs.meta.platforms or [ ])}
|
||||
@@ -517,24 +479,12 @@ let
|
||||
else if hasDisallowedInsecure attrs then
|
||||
{
|
||||
reason = "insecure";
|
||||
errormsg = "is marked as insecure";
|
||||
msg = "is marked as insecure";
|
||||
remediation = remediate_insecure attrs;
|
||||
}
|
||||
else
|
||||
null;
|
||||
|
||||
# Please also update the type in /pkgs/top-level/config.nix alongside this.
|
||||
checkWarnings =
|
||||
attrs:
|
||||
if hasNoMaintainers attrs then
|
||||
{
|
||||
reason = "maintainerless";
|
||||
errormsg = "has no maintainers or teams";
|
||||
remediation = "";
|
||||
}
|
||||
else
|
||||
null;
|
||||
|
||||
# Helper functions and declarations to handle identifiers, extracted to reduce allocations
|
||||
hasAllCPEParts =
|
||||
cpeParts:
|
||||
@@ -695,54 +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
|
||||
if isNull problems then
|
||||
{
|
||||
valid = "yes";
|
||||
handled = true;
|
||||
}
|
||||
else
|
||||
let
|
||||
msg =
|
||||
if inHydra then
|
||||
"Warning while evaluating ${getNameWithVersion attrs}: «${warning.reason}»: ${warning.errormsg}"
|
||||
else
|
||||
"Package ${getNameWithVersion attrs} in ${pos_str meta} ${warning.errormsg}, continuing anyway."
|
||||
+ (optionalString (warning.remediation != "") "\n${warning.remediation}");
|
||||
|
||||
handled = if elem warning.reason showWarnings then trace msg true else true;
|
||||
in
|
||||
warning
|
||||
// {
|
||||
valid = "warn";
|
||||
handled = handled;
|
||||
{
|
||||
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.errormsg}"
|
||||
else
|
||||
''
|
||||
Package ‘${getNameWithVersion attrs}’ in ${pos_str meta} ${invalid.errormsg}, refusing to evaluate.
|
||||
|
||||
''
|
||||
+ invalid.remediation;
|
||||
|
||||
handled = if config ? handleEvalIssue then config.handleEvalIssue invalid.reason msg else throw msg;
|
||||
in
|
||||
invalid
|
||||
// {
|
||||
{
|
||||
valid = "no";
|
||||
handled = handled;
|
||||
handled = handle {
|
||||
inherit attrs meta;
|
||||
error = invalid;
|
||||
};
|
||||
};
|
||||
|
||||
in
|
||||
|
||||
@@ -5,23 +5,47 @@
|
||||
# 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
|
||||
isList
|
||||
all
|
||||
any
|
||||
attrNames
|
||||
attrValues
|
||||
concatMap
|
||||
isFunction
|
||||
isBool
|
||||
concatStringsSep
|
||||
concatMapStringsSep
|
||||
isFloat
|
||||
elem
|
||||
mapAttrs
|
||||
;
|
||||
isTypeDef = t: isAttrs t && t ? name && isString t.name && t ? verify && isFunction t.verify;
|
||||
|
||||
in
|
||||
lib.fix (self: {
|
||||
|
||||
/*
|
||||
`errors type "<context>" value` gives a list of string error messages,
|
||||
each prefixed with "<context>: ", for why `value` is not of type `type`
|
||||
|
||||
Only use this if `type.verify value` is false
|
||||
|
||||
Types can override this by specifying their own `type.errors = ctx: value:` attribute
|
||||
|
||||
This is intentionally not tied into `type.verify`,
|
||||
in order to keep the successful path as fast as possible with minimal allocations
|
||||
*/
|
||||
errors =
|
||||
t:
|
||||
t.errors or (ctx: v: [
|
||||
"${ctx}: Invalid value; expected ${t.name}, got\n ${
|
||||
lib.generators.toPretty { indent = " "; } v
|
||||
}"
|
||||
]);
|
||||
|
||||
string = {
|
||||
name = "string";
|
||||
verify = isString;
|
||||
@@ -69,6 +93,14 @@ lib.fix (self: {
|
||||
verify =
|
||||
# attrsOf<any> can be optimised to just isAttrs
|
||||
if t == self.any then isAttrs else attrs: isAttrs attrs && all verify (attrValues attrs);
|
||||
errors =
|
||||
ctx: attrs:
|
||||
if !isAttrs attrs then
|
||||
self.errors self.attrs ctx attrs
|
||||
else
|
||||
concatMap (
|
||||
name: lib.optionals (!verify attrs.${name}) (self.errors t "${ctx}.${name}" attrs.${name})
|
||||
) (attrNames attrs);
|
||||
};
|
||||
|
||||
listOf =
|
||||
@@ -82,6 +114,19 @@ lib.fix (self: {
|
||||
verify =
|
||||
# listOf<any> can be optimised to just isList
|
||||
if t == self.any then isList else v: isList v && all verify v;
|
||||
errors =
|
||||
ctx: v:
|
||||
if !isList v then
|
||||
self.errors self.list ctx v
|
||||
else
|
||||
lib.concatMap ({ valid, errors }: errors) (
|
||||
lib.filter ({ valid, errors }: !valid) (
|
||||
lib.imap0 (i: el: {
|
||||
valid = verify el;
|
||||
errors = self.errors t "${ctx}.${toString i}" el;
|
||||
}) v
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
union =
|
||||
@@ -95,4 +140,41 @@ lib.fix (self: {
|
||||
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;
|
||||
};
|
||||
|
||||
record =
|
||||
fields:
|
||||
assert isAttrs fields && all isTypeDef (attrValues fields);
|
||||
let
|
||||
# Map attrs directly to the verify function for performance
|
||||
fieldVerifiers = mapAttrs (_: t: t.verify) fields;
|
||||
in
|
||||
{
|
||||
name = "record";
|
||||
verify = v: isAttrs v && all (k: fieldVerifiers ? ${k} && fieldVerifiers.${k} v.${k}) (attrNames v);
|
||||
errors =
|
||||
ctx: v:
|
||||
if !isAttrs v then
|
||||
self.errors self.attrs ctx v
|
||||
else
|
||||
concatMap (
|
||||
k:
|
||||
if fieldVerifiers ? ${k} then
|
||||
lib.optionals (!fieldVerifiers.${k} v.${k}) (self.errors fields.${k} "${ctx}.${k}" v.${k})
|
||||
else
|
||||
[
|
||||
"${ctx}: key '${k}' is unrecognized; expected one of: \n [${
|
||||
concatMapStringsSep ", " (x: "'${x}'") (attrNames fields)
|
||||
}]"
|
||||
]
|
||||
) (attrNames v);
|
||||
};
|
||||
|
||||
})
|
||||
|
||||
@@ -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 <link xlink:href="https://nixos.org/manual/nixpkgs/stable/#sec-ignore-problems">Installing packages with problems</link> 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.<package> = {
|
||||
nameSpecific.<name> = {
|
||||
kindSpecific.<kind> = <ignore|warn|error>;
|
||||
kindFallback = <ignore|warn|error>;
|
||||
};
|
||||
nameFallback = {
|
||||
kindSpecific.<kind> = <ignore|warn|error>;
|
||||
kindFallback = <ignore|warn|error>;
|
||||
};
|
||||
};
|
||||
packageFallback = {
|
||||
nameSpecific.<name> = {
|
||||
kindSpecific.<kind> = <ignore|warn|error>;
|
||||
kindFallback = <ignore|warn|error>;
|
||||
};
|
||||
nameFallback = {
|
||||
kindSpecific.<kind> = <ignore|warn|error>;
|
||||
kindFallback = <ignore|warn|error>;
|
||||
};
|
||||
};
|
||||
|
||||
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 = <below code>; }
|
||||
- NixOS: nixpkgs.config = <below code>;
|
||||
- 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;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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 { };
|
||||
|
||||
@@ -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 = [ ];
|
||||
}
|
||||
+20
@@ -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 = <below code>; }
|
||||
- NixOS: nixpkgs.config = <below code>;
|
||||
- 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"
|
||||
};
|
||||
}
|
||||
@@ -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" ];
|
||||
}
|
||||
@@ -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 = <below code>; }
|
||||
- NixOS: nixpkgs.config = <below code>;
|
||||
- nix-* commands: Put below code in ~/.config/nixpkgs/config.nix
|
||||
|
||||
{
|
||||
problems.handlers = {
|
||||
a.deprecated = "warn"; # or "ignore"
|
||||
a.removal = "warn"; # or "ignore"
|
||||
};
|
||||
}
|
||||
@@ -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" ];
|
||||
}
|
||||
@@ -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 = <below code>; }
|
||||
- NixOS: nixpkgs.config = <below code>;
|
||||
- nix-* commands: Put below code in ~/.config/nixpkgs/config.nix
|
||||
|
||||
{
|
||||
problems.handlers = {
|
||||
a.deprecated = "warn"; # or "ignore"
|
||||
};
|
||||
}
|
||||
@@ -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" ];
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
warning: you did not specify '--add-root'; the result might be removed by the garbage collector
|
||||
@@ -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" ];
|
||||
}
|
||||
@@ -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
|
||||
@@ -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";
|
||||
};
|
||||
}
|
||||
@@ -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<removal,deprecated>. You can specify an explicit problem kind with `a.meta.problems.invalid.kind`
|
||||
@@ -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.";
|
||||
};
|
||||
}
|
||||
+4
@@ -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
|
||||
@@ -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.";
|
||||
}
|
||||
@@ -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
|
||||
@@ -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 = [ ];
|
||||
}
|
||||
@@ -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 = <below code>; }
|
||||
- NixOS: nixpkgs.config = <below code>;
|
||||
- nix-* commands: Put below code in ~/.config/nixpkgs/config.nix
|
||||
|
||||
{
|
||||
problems.handlers = {
|
||||
a.maintainerless = "warn"; # or "ignore"
|
||||
};
|
||||
}
|
||||
@@ -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 = [ ];
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
warning: you did not specify '--add-root'; the result might be removed by the garbage collector
|
||||
@@ -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 = [ ];
|
||||
}
|
||||
@@ -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
|
||||
@@ -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 = { };
|
||||
}
|
||||
@@ -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
|
||||
@@ -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";
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
warning: you did not specify '--add-root'; the result might be removed by the garbage collector
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
warning: you did not specify '--add-root'; the result might be removed by the garbage collector
|
||||
@@ -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";
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
warning: you did not specify '--add-root'; the result might be removed by the garbage collector
|
||||
@@ -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";
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
warning: you did not specify '--add-root'; the result might be removed by the garbage collector
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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";
|
||||
}
|
||||
@@ -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";
|
||||
@@ -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"
|
||||
];
|
||||
};
|
||||
}
|
||||
@@ -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 = <below code>; }
|
||||
- NixOS: nixpkgs.config = <below code>;
|
||||
- nix-* commands: Put below code in ~/.config/nixpkgs/config.nix
|
||||
|
||||
{
|
||||
problems.handlers = {
|
||||
a.removal = "warn"; # or "ignore"
|
||||
};
|
||||
}
|
||||
@@ -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.";
|
||||
};
|
||||
}
|
||||
@@ -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 = <below code>; }
|
||||
- NixOS: nixpkgs.config = <below code>;
|
||||
- nix-* commands: Put below code in ~/.config/nixpkgs/config.nix
|
||||
|
||||
{
|
||||
problems.handlers = {
|
||||
a.removal = "warn"; # or "ignore"
|
||||
};
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
warning: you did not specify '--add-root'; the result might be removed by the garbage collector
|
||||
@@ -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";
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -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
|
||||
@@ -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.";
|
||||
}
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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 = { };
|
||||
};
|
||||
}
|
||||
@@ -45,11 +45,18 @@ let
|
||||
internal = true;
|
||||
};
|
||||
|
||||
# Should be replaced by importing <nixos/modules/misc/assertions.nix> 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";
|
||||
})
|
||||
];
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user