Skip to content

Commit

Permalink
stdenv/check-meta: do deep type checks
Browse files Browse the repository at this point in the history
Use a wrapper around `mergeDefinitions` to type-check values deeply, so
that e.g. `maintainers = [ 42 ];` is an error.
  • Loading branch information
ncfavier committed Jan 1, 2023
1 parent 4df10de commit 4af22aa
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 29 deletions.
6 changes: 4 additions & 2 deletions lib/generators.nix
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,9 @@ rec {
(This means fn is type Val -> String.) */
allowPrettyValues ? false,
/* If this option is true, the output is indented with newlines for attribute sets and lists */
multiline ? true
multiline ? true,
/* Initial indentation level */
indent ? ""
}:
let
go = indent: v: with builtins;
Expand Down Expand Up @@ -348,7 +350,7 @@ rec {
};") v)
+ outroSpace + "}"
else abort "generators.toPretty: should never happen (v = ${v})";
in go "";
in go indent;

# PLIST handling
toPlist = {}: v: let
Expand Down
58 changes: 31 additions & 27 deletions pkgs/stdenv/generic/check-meta.nix
Original file line number Diff line number Diff line change
Expand Up @@ -247,16 +247,16 @@ let
isEnabled = lib.findFirst (x: x == reason) null showWarnings;
in if isEnabled != null then builtins.trace msg true else true;


# A shallow type check. We are using NixOS'
# option types here, which however have the major drawback
# of not providing full type checking (part of the type check is
# done by the module evaluation itself). Therefore, the checks
# will not recurse into attributes.
# We still provide the full type for documentation
# purposes and in the hope that they will be used eventually.
# See https://github.com/NixOS/nixpkgs/pull/191171 for an attempt
# to fix this, or mkOptionType in lib/types.nix for more information.
# 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;

# TODO make this into a proper module and use the generic option documentation generation?
metaTypes = with lib.types; rec {
# These keys are documented
description = str;
Expand All @@ -266,9 +266,11 @@ let
homepage = either (listOf str) str;
downloadPage = str;
changelog = either (listOf str) str;
license = either (listOf lib.types.attrs) (either lib.types.attrs str);
sourceProvenance = either (listOf lib.types.attrs) lib.types.attrs;
maintainers = listOf (attrsOf str);
license = let
licenseType = either (attrsOf anything) str; # TODO disallow `str` licenses, use a module
in either licenseType (listOf licenseType);
sourceProvenance = either (listOf (attrsOf anything)) (attrsOf anything);
maintainers = listOf (attrsOf anything); # TODO use the maintainer type from lib/tests/maintainer-module.nix
priority = int;
platforms = listOf str;
hydraPlatforms = listOf str;
Expand Down Expand Up @@ -310,16 +312,16 @@ let
badPlatforms = platforms;
};

# WARNING: this does not check inner values of the attribute, like list elements or nested attributes.
# See metaTypes above and mkOptionType in lib/types.nix for more information
checkMetaAttr = k: v:
if metaTypes?${k} then
if metaTypes.${k}.check v then
if typeCheck metaTypes.${k} v then
null
else
"key 'meta.${k}' has a value of invalid type ${builtins.typeOf v}; expected ${metaTypes.${k}.description}"
"key 'meta.${k}' has invalid value; expected ${metaTypes.${k}.description}, got\n ${
lib.generators.toPretty { indent = " "; } v
}"
else
"key 'meta.${k}' is unrecognized; expected one of: \n\t [${lib.concatMapStringsSep ", " (x: "'${x}'") (lib.attrNames metaTypes)}]";
"key 'meta.${k}' is unrecognized; expected one of: \n [${lib.concatMapStringsSep ", " (x: "'${x}'") (lib.attrNames metaTypes)}]";
checkMeta = meta: if config.checkMeta then lib.remove null (lib.mapAttrsToList checkMetaAttr meta) else [];

checkOutputsToInstall = attrs: let
Expand All @@ -333,25 +335,27 @@ let
# Check if a derivation is valid, that is whether it passes checks for
# e.g brokenness or license.
#
# Return { valid: Bool } and additionally
# Return { valid: "yes", "warn" or "no" } and additionally
# { reason: String; errormsg: 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
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:${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 an invalid meta attrset:${lib.concatMapStrings (x: "\n\t - " + x) res}"; }
} // (
# --- Put checks that cannot be ignored here ---
else if checkOutputsToInstall attrs then
if checkOutputsToInstall attrs then
{ valid = "no"; reason = "broken-outputs"; errormsg = "has invalid meta.outputsToInstall"; }

# --- Put checks that can be ignored here ---
Expand Down

0 comments on commit 4af22aa

Please sign in to comment.