From 950ac2bc8fa13a7557d931123c24c93418662a2d Mon Sep 17 00:00:00 2001 From: Daiderd Jordan Date: Sun, 19 Apr 2020 12:04:58 +0200 Subject: [PATCH] meta: expose availability flags in derivation metadata Currently it's not possible to determine the reason why a package is unavailable without evaluating nixpkgs multiple times with different settings. eg. nix-repl> :p android-studio.meta { available = false; broken = false; unfree = true; unsupported = true; ... } The following snippet is an example that uses this information to query the availability information of all packages in nixpkgs, giving an overview of all the packages currently marked as broken, etc. { pkgs }: with import ; let mapPkgs = let mapPkgs' = path: f: mapAttrs (n: v: let result = builtins.tryEval (v ? meta); in if !result.success then {} else if isDerivation v then f (path ++ [n]) v else if isAttrs v && v.recurseForDerivations or false then mapPkgs' (path ++ [n]) f v else {} ); in mapPkgs' []; getMeta = path: drv: if drv.meta ? available then let meta = { pkg = concatStringsSep "." path; inherit (drv.meta) broken unfree unsupported insecure; }; in builtins.trace meta.pkg meta else {}; metaToList = attrs: flatten (map (v: if v ? pkg then v else metaToList v) (attrValues attrs)); in metaToList (mapPkgs getMeta pkgs) --- pkgs/stdenv/generic/check-meta.nix | 39 ++++++++++++++++++------- pkgs/stdenv/generic/make-derivation.nix | 3 +- 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/pkgs/stdenv/generic/check-meta.nix b/pkgs/stdenv/generic/check-meta.nix index 21ae809a2225..e6e90a6df1e1 100644 --- a/pkgs/stdenv/generic/check-meta.nix +++ b/pkgs/stdenv/generic/check-meta.nix @@ -49,6 +49,18 @@ let isUnfree = licenses: lib.lists.any (l: !l.free or true) licenses; + hasUnfreeLicense = attrs: + hasLicense attrs && + isUnfree (lib.lists.toList attrs.meta.license); + + isMarkedBroken = attrs: attrs.meta.broken or false; + + hasUnsupportedPlatform = attrs: + (!lib.lists.elem hostPlatform.system (attrs.meta.platforms or lib.platforms.all) || + lib.lists.elem hostPlatform.system (attrs.meta.badPlatforms or [])); + + isMarkedInsecure = attrs: (attrs.meta.knownVulnerabilities or []) != []; + # Alow granular checks to allow only some unfree packages # Example: # {pkgs, ...}: @@ -62,16 +74,15 @@ let # package has an unfree license and is not explicitely allowed by the # `allowUnfreePredicate` function. hasDeniedUnfreeLicense = attrs: + hasUnfreeLicense attrs && !allowUnfree && - hasLicense attrs && - isUnfree (lib.lists.toList attrs.meta.license) && !allowUnfreePredicate attrs; allowInsecureDefaultPredicate = x: builtins.elem (getName x) (config.permittedInsecurePackages or []); allowInsecurePredicate = x: (config.allowInsecurePredicate or allowInsecureDefaultPredicate) x; hasAllowedInsecure = attrs: - (attrs.meta.knownVulnerabilities or []) == [] || + !(isMarkedInsecure attrs) || allowInsecurePredicate attrs || builtins.getEnv "NIXPKGS_ALLOW_INSECURE" == "1"; @@ -173,6 +184,9 @@ let platforms = listOf str; hydraPlatforms = listOf str; broken = bool; + unfree = bool; + unsupported = bool; + insecure = bool; # 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 = attrsOf (mkOptionType { @@ -224,17 +238,22 @@ let # # Return { valid: Bool } and additionally # { reason: String; errormsg: String } if it is not valid, where - # reason is one of "unfree", "blacklisted" or "broken". + # reason is one of "unfree", "blacklisted", "broken", "insecure", ... + # Along with a boolean flag for each reason checkValidity = attrs: - if hasDeniedUnfreeLicense attrs && !(hasWhitelistedLicense attrs) then + { + unfree = hasUnfreeLicense attrs; + broken = isMarkedBroken attrs; + unsupported = hasUnsupportedPlatform attrs; + insecure = isMarkedInsecure attrs; + } + // (if hasDeniedUnfreeLicense attrs && !(hasWhitelistedLicense attrs) then { valid = false; reason = "unfree"; errormsg = "has an unfree license (‘${showLicense attrs.meta.license}’)"; } else if hasBlacklistedLicense attrs then { valid = false; reason = "blacklisted"; errormsg = "has a blacklisted license (‘${showLicense attrs.meta.license}’)"; } else if !allowBroken && attrs.meta.broken or false then { valid = false; reason = "broken"; errormsg = "is marked as broken"; } - else if !allowUnsupportedSystem && - (!lib.lists.elem hostPlatform.system (attrs.meta.platforms or lib.platforms.all) || - lib.lists.elem hostPlatform.system (attrs.meta.badPlatforms or [])) then + else if !allowUnsupportedSystem && hasUnsupportedPlatform attrs then { valid = false; reason = "unsupported"; errormsg = "is not supported on ‘${hostPlatform.system}’"; } else if !(hasAllowedInsecure attrs) then { valid = false; reason = "insecure"; errormsg = "is marked as insecure"; } @@ -242,14 +261,14 @@ let { valid = false; reason = "broken-outputs"; errormsg = "has invalid meta.outputsToInstall"; } else let res = checkMeta (attrs.meta or {}); in if res != [] then { valid = false; reason = "unknown-meta"; errormsg = "has an invalid meta attrset:${lib.concatMapStrings (x: "\n\t - " + x) res}"; } - else { valid = true; }; + else { valid = true; }); assertValidity = { meta, attrs }: let validity = checkValidity attrs; in validity // { # Throw an error if trying to evaluate an non-valid derivation handled = if !validity.valid - then handleEvalIssue { inherit meta attrs; } (removeAttrs validity ["valid"]) + then handleEvalIssue { inherit meta attrs; } { inherit (validity) reason errormsg; } else true; }; diff --git a/pkgs/stdenv/generic/make-derivation.nix b/pkgs/stdenv/generic/make-derivation.nix index a11b280b047e..7eb41b314dbb 100644 --- a/pkgs/stdenv/generic/make-derivation.nix +++ b/pkgs/stdenv/generic/make-derivation.nix @@ -302,8 +302,9 @@ in rec { # Fill `meta.position` to identify the source location of the package. // lib.optionalAttrs (pos != null) { position = pos.file + ":" + toString pos.line; - # Expose the result of the checks for everyone to see. } // { + # Expose the result of the checks for everyone to see. + inherit (validity) unfree broken unsupported insecure; available = validity.valid && (if config.checkMetaRecursively or false then lib.all (d: d.meta.available or true) references