From eba1d513522ab485ec21874498f72e8b011b576a Mon Sep 17 00:00:00 2001 From: Silvan Mosberger Date: Fri, 19 Jun 2026 15:10:05 +0200 Subject: [PATCH 1/6] meta.problems: Internal refactoring Makes future changes easier --- pkgs/stdenv/generic/problems.nix | 131 +++++++++--------- .../cases/invalid-kind-error/expected-stderr | 2 +- 2 files changed, 69 insertions(+), 64 deletions(-) diff --git a/pkgs/stdenv/generic/problems.nix b/pkgs/stdenv/generic/problems.nix index 0f03c393e384a..1451e6161c90a 100644 --- a/pkgs/stdenv/generic/problems.nix +++ b/pkgs/stdenv/generic/problems.nix @@ -72,68 +72,73 @@ rec { 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" - "broken" - ]; - # Problem kinds that are currently only allowed to be specified once - unique = [ - "removal" - ]; + kinds = { + maintainerless = { + manualAllowed = false; + isUnique = false; + automatic = { + 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. + config: 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."; + }; + }; + broken = { + manualAllowed = true; + isUnique = false; + automatic = { + condition = + config: + let + # TODO: Consider deprecating this or making it generic for all problems + allowBroken = config.allowBroken || builtins.getEnv "NIXPKGS_ALLOW_BROKEN" == "1"; - # Same thing but a set with null values (comes in handy at times) - manual' = genAttrs manual (k: null); - unique' = genAttrs unique (k: null); + allowBrokenPredicate = + lib.warnIf (lib.oldestSupportedReleaseIsAtLeast 2605) + "config.allowBrokenPredicate is deprecated, use config.problems.handlers.myPackage.broken = \"warn\" for individual packages instead." + config.allowBrokenPredicate; + in + if allowBroken then + attrs: false + else if config ? allowBrokenPredicate then + attrs: attrs ? meta.broken && attrs.meta.broken && !allowBrokenPredicate attrs + else + attrs: attrs ? meta.broken && attrs.meta.broken; + value.message = "This package is broken."; + }; + }; + removal = { + manualAllowed = true; + isUnique = true; + automatic = null; + }; + deprecated = { + manualAllowed = true; + isUnique = false; + automatic = 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. - config: 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."; - } - { - kindName = "broken"; - condition = - config: - let - # TODO: Consider deprecating this or making it generic for all problems - allowBroken = config.allowBroken || builtins.getEnv "NIXPKGS_ALLOW_BROKEN" == "1"; + # Problem kinds that are currently allowed to be specified in `meta.problems` + manualKinds = lib.filterAttrs (name: value: value.manualAllowed) kinds; + # Problem kinds that are currently only allowed to be specified once + uniqueKinds = lib.filterAttrs (name: value: value.isUnique) kinds; - allowBrokenPredicate = - lib.warnIf (lib.oldestSupportedReleaseIsAtLeast 2605) - "config.allowBrokenPredicate is deprecated, use config.problems.handlers.myPackage.broken = \"warn\" for individual packages instead." - config.allowBrokenPredicate; - in - if allowBroken then - attrs: false - else if config ? allowBrokenPredicate then - attrs: attrs ? meta.broken && attrs.meta.broken && !allowBrokenPredicate attrs - else - attrs: attrs ? meta.broken && attrs.meta.broken; - value.message = "This package is broken."; - } - ]; + automaticProblems = lib.mapAttrsToList (name: value: value.automatic // { kindName = name; }) ( + lib.filterAttrs (name: value: value.automatic != null) kinds + ); genAutomaticProblems = config: attrs: @@ -148,7 +153,7 @@ rec { let types = lib.types; handlerType = types.enum handlers.levels; - problemKindType = types.enum kinds.known; + problemKindType = types.enum (attrNames kinds); in { handlers = lib.mkOption { @@ -250,7 +255,7 @@ rec { record enum ; - kindType = enum kinds.manual; + kindType = enum (attrNames manualKinds); subRecord = record { kind = kindType; message = str; @@ -270,7 +275,7 @@ rec { 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)) ( + all (kind: manualKinds ? ${kind} && (uniqueKinds ? ${kind} -> length kindGroups.${kind} == 1)) ( attrNames kindGroups ) ); @@ -294,14 +299,14 @@ rec { ++ concatLists ( mapAttrsToList ( kind: kindGroup: - optionals (!kinds.manual' ? ${kind}) ( + optionals (!manualKinds ? ${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) + optional (uniqueKinds ? ${kind} && length kindGroup > 1) "${ctx}: Problem kind ${kind} should be unique, but is used for these problems: ${ concatMapStringsSep ", " (el: el.name) kindGroup }" diff --git a/pkgs/test/problems/cases/invalid-kind-error/expected-stderr b/pkgs/test/problems/cases/invalid-kind-error/expected-stderr index 07c5a38e1bcb7..6fdf2b1d04b19 100644 --- a/pkgs/test/problems/cases/invalid-kind-error/expected-stderr +++ b/pkgs/test/problems/cases/invalid-kind-error/expected-stderr @@ -1,4 +1,4 @@ (stack trace truncated; use '--show-trace' to show the full, detailed trace) error: Refusing to evaluate package 'a-0' in /nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-default.nix:11 because it has an invalid meta attrset: - - a.meta.problems.invalid: Problem kind invalid, inferred from the problem name, is invalid; expected enum. You can specify an explicit problem kind with `a.meta.problems.invalid.kind` + - a.meta.problems.invalid: Problem kind invalid, inferred from the problem name, is invalid; expected enum. You can specify an explicit problem kind with `a.meta.problems.invalid.kind` From 8fcbb3592984e1f364ec561f491790d854704264 Mon Sep 17 00:00:00 2001 From: Silvan Mosberger Date: Fri, 19 Jun 2026 16:37:00 +0200 Subject: [PATCH 2/6] meta.problems: Fill out output meta with final list of problems This ensures that external tooling can also process the problems, and helps users with debugging. --- pkgs/stdenv/generic/check-meta.nix | 3 +++ pkgs/stdenv/generic/problems.nix | 13 ++++++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/pkgs/stdenv/generic/check-meta.nix b/pkgs/stdenv/generic/check-meta.nix index 70c009c3e83ae..071bfba324520 100644 --- a/pkgs/stdenv/generic/check-meta.nix +++ b/pkgs/stdenv/generic/check-meta.nix @@ -49,6 +49,7 @@ let inherit (import ./problems.nix { inherit lib; }) problemsType genCheckProblems + completeMetaProblems ; checkProblems = genCheckProblems config; @@ -670,6 +671,8 @@ let unsupported = hasUnsupportedPlatform' attrs; insecure = isMarkedInsecure attrs; + problems = completeMetaProblems config attrs; + available = validity.valid != "no" && ((config.checkMetaRecursively or false) -> all (d: d.meta.available or true) references); diff --git a/pkgs/stdenv/generic/problems.nix b/pkgs/stdenv/generic/problems.nix index 1451e6161c90a..c7b6754d729d5 100644 --- a/pkgs/stdenv/generic/problems.nix +++ b/pkgs/stdenv/generic/problems.nix @@ -255,7 +255,8 @@ rec { record enum ; - kindType = enum (attrNames manualKinds); + # While we should only allow manual kinds, we need to allow `meta.problems = otherPackage.meta.problems`, which includes automatic ones as well + kindType = enum (attrNames kinds); subRecord = record { kind = kindType; message = str; @@ -275,7 +276,7 @@ rec { let kindGroups = groupBy (kind: kind) (mapAttrsToList (name: problem: problem.kind or name) v); in - all (kind: manualKinds ? ${kind} && (uniqueKinds ? ${kind} -> length kindGroups.${kind} == 1)) ( + all (kind: kinds ? ${kind} && (uniqueKinds ? ${kind} -> length kindGroups.${kind} == 1)) ( attrNames kindGroups ) ); @@ -299,7 +300,7 @@ rec { ++ concatLists ( mapAttrsToList ( kind: kindGroup: - optionals (!manualKinds ? ${kind}) ( + optionals (!kinds ? ${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`" @@ -515,6 +516,12 @@ rec { in processProblems pname problemsToHandle; + completeMetaProblems = + config: attrs: + mapAttrs (name: problem: { kind = name; } // problem) ( + (attrs.meta.problems or { }) // genAutomaticProblems config attrs + ); + processProblems = pname: problemsToHandle: let From 7cc4d7dea8843ae0176e465f333587e0b657b042 Mon Sep 17 00:00:00 2001 From: Silvan Mosberger Date: Wed, 17 Jun 2026 15:26:57 +0200 Subject: [PATCH 3/6] config: Introduce attrPathsDisallowedForInternalUse This is very similar to how the aliases.nix overlay can be removed to prevent usage of them in Nixpkgs itself, but this config attribute allows preventing use of other existing attributes in Nixpkgs. Needed for a follow-up commit. --- pkgs/top-level/config.nix | 28 ++++++++++++++++++++++++++++ pkgs/top-level/default.nix | 23 ++++++++++++++++++++++- pkgs/top-level/stage.nix | 26 ++++++++++++++++++++++++++ 3 files changed, 76 insertions(+), 1 deletion(-) diff --git a/pkgs/top-level/config.nix b/pkgs/top-level/config.nix index f2066315efb5e..de13c0a29993d 100644 --- a/pkgs/top-level/config.nix +++ b/pkgs/top-level/config.nix @@ -58,6 +58,34 @@ let internal = true; }; + attrPathsDisallowedForInternalUse = mkOption { + type = types.listOf ( + types.submodule { + options.attrPath = lib.mkOption { + type = types.listOf types.str; + description = '' + Attribute path to disallow. + ''; + }; + options.reason = lib.mkOption { + type = types.nullOr types.str; + default = null; + example = /* because */ "it's dangerous."; + description = '' + Reason for it being disallowed. + ''; + }; + } + ); + internal = true; + default = [ ]; + description = '' + List of attribute paths that may not be used by other packages in Nixpkgs. + + Should usually only be defined by Nixpkgs CI. + ''; + }; + # Config options warnUndeclaredOptions = mkOption { diff --git a/pkgs/top-level/default.nix b/pkgs/top-level/default.nix index 443496f650fb6..4d44abadd961b 100644 --- a/pkgs/top-level/default.nix +++ b/pkgs/top-level/default.nix @@ -214,7 +214,28 @@ let ; }; - pkgs = boot stages; + fixedPoint = boot stages; + + pkgs = + if config.attrPathsDisallowedForInternalUse == [ ] then + fixedPoint + else + # See ./stage.nix, which replaced config.attrPathsDisallowedForInternalUse with aborts. + # We replace these attribute paths with their original derivations again, + # because CI would just error out from the aborting attributes themselves. + # Internally all packages still see the aborting attributes if used as dependencies, + # because we do this here after the fixed-point is calculated. + # Note that we don't want to remove the attributes entirely like what aliases.nix does, + # because unlike aliases, CI still needs to check the packages to evaluate at all, + # which it wouldn't if they're removed entirely. + lib.updateManyAttrsByPath + (map (attrs: { + path = attrs.attrPath; + update = + _: + lib.getAttrFromPath attrs.attrPath fixedPoint.__internalBeforeInternallyDisallowedAttrPathsOverlay; + }) config.attrPathsDisallowedForInternalUse) + (removeAttrs fixedPoint [ "__internalBeforeInternallyDisallowedAttrPathsOverlay" ]); in checked pkgs diff --git a/pkgs/top-level/stage.nix b/pkgs/top-level/stage.nix index 4b0abd0361c6c..35929783e7c95 100644 --- a/pkgs/top-level/stage.nix +++ b/pkgs/top-level/stage.nix @@ -309,6 +309,31 @@ let }; }; + # Replaces the attributes in config.attrPathsDisallowedForInternalUse with aborts. + # Not throws because those would be ignored by nix-env, which is what CI uses to evaluate everything + # See also ./default.nix, where these attributes are added back again so they're still checked by CI + internallyDisallowedAttrPathsOverlay = + final: prev: + if config.attrPathsDisallowedForInternalUse == [ ] then + { } + else + { + # So that ./default.nix can add them back again outside the fixed point + # Don't use this in packages! + __internalBeforeInternallyDisallowedAttrPathsOverlay = prev; + } + // lib.updateManyAttrsByPath (map ( + { attrPath, reason }: + { + path = attrPath; + update = + _: + abort "${lib.concatStringsSep "." attrPath} is disallowed from being used within Nixpkgs${ + lib.optionalString (reason != null) ", because ${reason}" + }"; + } + ) config.attrPathsDisallowedForInternalUse) prev; + # The complete chain of package set builders, applied from top to bottom. # stdenvOverlays must be last as it brings package forward from the # previous bootstrapping phases which have already been overlaid. @@ -324,6 +349,7 @@ let aliases variants configOverrides + internallyDisallowedAttrPathsOverlay ] ++ overlays ++ [ From 19a31658dc39324c9acf8d81198cd7137bdc1e92 Mon Sep 17 00:00:00 2001 From: Silvan Mosberger Date: Fri, 19 Jun 2026 16:59:30 +0200 Subject: [PATCH 4/6] ci/eval: Refactor attrpaths.nix to a more generic pre-eval.nix --- ci/eval/chunk.nix | 8 ++++---- ci/eval/default.nix | 26 ++++++++++++------------- ci/eval/outpaths.nix | 2 +- ci/eval/{attrpaths.nix => pre-eval.nix} | 13 ++++++++----- 4 files changed, 26 insertions(+), 23 deletions(-) rename ci/eval/{attrpaths.nix => pre-eval.nix} (84%) diff --git a/ci/eval/chunk.nix b/ci/eval/chunk.nix index e1759c2c6615f..4fabea659ad76 100644 --- a/ci/eval/chunk.nix +++ b/ci/eval/chunk.nix @@ -2,8 +2,8 @@ { lib ? import ../../lib, path ? ../.., - # The file containing all available attribute paths, which are split into chunks here - attrpathFile, + # The file containing the preEval result + preEvalFile, chunkSize, myChunk, includeBroken, @@ -12,8 +12,8 @@ }: let - attrpaths = lib.importJSON attrpathFile; - myAttrpaths = lib.sublist (chunkSize * myChunk) chunkSize attrpaths; + preEvalResult = lib.importJSON preEvalFile; + myAttrpaths = lib.sublist (chunkSize * myChunk) chunkSize preEvalResult.paths; unfiltered = import ./outpaths.nix { inherit path; diff --git a/ci/eval/default.nix b/ci/eval/default.nix index 2e8bec41ca5d3..547fcf1f26be0 100644 --- a/ci/eval/default.nix +++ b/ci/eval/default.nix @@ -38,7 +38,7 @@ let fileset = unions ( map (lib.path.append ../..) [ ".version" - "ci/eval/attrpaths.nix" + "ci/eval/pre-eval.nix" "ci/eval/chunk.nix" "ci/eval/outpaths.nix" "default.nix" @@ -56,11 +56,11 @@ let builtins.readFile ../../pkgs/top-level/release-supported-systems.json ); - attrpathsSuperset = + preEval = { evalSystem, }: - runCommand "attrpaths-superset.json" + runCommand "pre-eval" { src = nixpkgs; # Don't depend on -dev outputs to reduce closure size for CI. @@ -73,15 +73,15 @@ let export NIX_STATE_DIR=$(mktemp -d) mkdir $out export GC_INITIAL_HEAP_SIZE=4g - command time -f "Attribute eval done [%MKB max resident, %Es elapsed] %C" \ + command time -f "Pre-eval done [%MKB max resident, %Es elapsed] %C" \ nix-instantiate --eval --strict --json --show-trace \ - "$src/ci/eval/attrpaths.nix" \ - -A paths \ + "$src/ci/eval/pre-eval.nix" \ + -A result \ -I "$src" \ --argstr extraNixpkgsConfigJson ${lib.escapeShellArg (builtins.toJSON extraNixpkgsConfig)} \ --option restrict-eval true \ --option allow-import-from-derivation false \ - --option eval-system "${evalSystem}" > $out/paths.json + --option eval-system "${evalSystem}" > $out/result.json ''; singleSystem = @@ -90,8 +90,8 @@ let # Note that this is intentionally not called `system`, # because `--argstr system` would only be passed to the ci/default.nix file! evalSystem ? builtins.currentSystem, - # The path to the `paths.json` file from `attrpathsSuperset` - attrpathFile ? "${attrpathsSuperset { inherit evalSystem; }}/paths.json", + # The path to the `result.json` file from `preEval` + preEvalFile ? "${preEval { inherit evalSystem; }}/result.json", }: let singleChunk = writeShellScript "single-chunk" '' @@ -121,12 +121,12 @@ let --show-trace \ --arg chunkSize "$chunkSize" \ --arg myChunk "$myChunk" \ - --arg attrpathFile "${attrpathFile}" \ + --arg preEvalFile "${preEvalFile}" \ --arg systems "[ \"$system\" ]" \ --arg includeBroken ${lib.boolToString includeBroken} \ --argstr extraNixpkgsConfigJson ${lib.escapeShellArg (builtins.toJSON extraNixpkgsConfig)} \ -I ${nixpkgs} \ - -I ${attrpathFile} \ + -I ${preEvalFile} \ > "$outputDir/result/$myChunk" \ 2> "$outputDir/stderr/$myChunk" exitCode=$? @@ -164,7 +164,7 @@ let echo "System: $evalSystem" cores=$NIX_BUILD_CORES echo "Cores: $cores" - attrCount=$(jq length "${attrpathFile}") + attrCount=$(jq '.paths | length' "${preEvalFile}") echo "Attribute count: $attrCount" echo "Chunk size: $chunkSize" # Same as `attrCount / chunkSize` but rounded up @@ -316,7 +316,7 @@ let in { inherit - attrpathsSuperset + preEval singleSystem diff combine diff --git a/ci/eval/outpaths.nix b/ci/eval/outpaths.nix index afbce48f33213..6fcd22fc74a6f 100755 --- a/ci/eval/outpaths.nix +++ b/ci/eval/outpaths.nix @@ -6,7 +6,7 @@ includeBroken ? true, # set this to false to exclude meta.broken packages from the output path ? ./../.., - # used by ./attrpaths.nix + # used by ./pre-eval.nix attrNamesOnly ? false, # Set this to `null` to build for builtins.currentSystem only diff --git a/ci/eval/attrpaths.nix b/ci/eval/pre-eval.nix similarity index 84% rename from ci/eval/attrpaths.nix rename to ci/eval/pre-eval.nix index ff1bdabbb3505..2d5a092747ad5 100644 --- a/ci/eval/attrpaths.nix +++ b/ci/eval/pre-eval.nix @@ -1,6 +1,5 @@ -# This expression will, as efficiently as possible, dump a -# *superset* of all attrpaths of derivations which might be -# part of a release on *any* platform. +# This file does a fast pre-evaluation of Nixpkgs to determine: +# - paths: A *superset* of all attrpaths of derivations which might be part of a release on *any* platform. # # This expression runs single-threaded under all current Nix # implementations, but much faster and with much less memory @@ -10,9 +9,9 @@ # $NUM_CORES batches and evaluate the outpaths separately for each # batch, in parallel. # -# To dump the attrnames: +# To dump the result: # -# nix-instantiate --eval --strict --json ci/eval/attrpaths.nix -A names +# nix-instantiate --eval --strict --json ci/eval/pre-eval.nix -A result # { lib ? import (path + "/lib"), @@ -81,5 +80,9 @@ let in { + # TODO: Do we still need these? Probably not inherit paths names; + result = { + inherit paths; + }; } From a90d993610ce555baedf4ef9b135a2eff941d868 Mon Sep 17 00:00:00 2001 From: Silvan Mosberger Date: Fri, 19 Jun 2026 17:00:08 +0200 Subject: [PATCH 5/6] ci/eval: Allow preventing internal Nixpkgs use of certain problem kinds --- ci/eval/chunk.nix | 1 + ci/eval/outpaths.nix | 18 ++++++ ci/eval/pre-eval.nix | 100 +++++++++++++++++++++---------- pkgs/stdenv/generic/problems.nix | 8 +++ pkgs/top-level/default.nix | 1 + pkgs/top-level/stage.nix | 1 + 6 files changed, 99 insertions(+), 30 deletions(-) diff --git a/ci/eval/chunk.nix b/ci/eval/chunk.nix index 4fabea659ad76..9bea26ce31805 100644 --- a/ci/eval/chunk.nix +++ b/ci/eval/chunk.nix @@ -18,6 +18,7 @@ let unfiltered = import ./outpaths.nix { inherit path; inherit includeBroken systems; + inherit (preEvalResult) attrPathsDisallowedForInternalUse; extraNixpkgsConfig = builtins.fromJSON extraNixpkgsConfigJson; }; diff --git a/ci/eval/outpaths.nix b/ci/eval/outpaths.nix index 6fcd22fc74a6f..512290ce450a8 100755 --- a/ci/eval/outpaths.nix +++ b/ci/eval/outpaths.nix @@ -14,6 +14,8 @@ builtins.readFile (path + "/pkgs/top-level/release-supported-systems.json") ), + attrPathsDisallowedForInternalUse ? [ ], + # Customize the config used to evaluate nixpkgs extraNixpkgsConfig ? { }, }: @@ -35,6 +37,22 @@ let allowVariants = !attrNamesOnly; checkMeta = true; + # We don't need to care about problems being caught using the + # standard mechanism, because any problems whose kind is not + # nixpkgsInternalUseAllowed cause the corresponding attributes to + # be disallowed entirely for internal use with + # attrPathsDisallowedForInternalUse, see also ./pre-eval.nix + problems.matchers = lib.mkForce [ + # We only need to set the broken handler to error, so that CI + # doesn't evaluate those. No reason it couldn't evaluate them + # afaik, but this is how it's been before. + { + kind = "broken"; + handler = "error"; + } + ]; + inherit attrPathsDisallowedForInternalUse; + # Silence the `x86_64-darwin` deprecation warning. allowDeprecatedx86_64Darwin = true; diff --git a/ci/eval/pre-eval.nix b/ci/eval/pre-eval.nix index 2d5a092747ad5..eff8204a7bd52 100644 --- a/ci/eval/pre-eval.nix +++ b/ci/eval/pre-eval.nix @@ -1,5 +1,6 @@ # This file does a fast pre-evaluation of Nixpkgs to determine: # - paths: A *superset* of all attrpaths of derivations which might be part of a release on *any* platform. +# - attrPathsDisallowedForInternalUse: Attribute paths whose meta.problems has problems whose kinds should not be used internally in Nixpkgs # # This expression runs single-threaded under all current Nix # implementations, but much faster and with much less memory @@ -23,21 +24,25 @@ let # TODO: Use mapAttrsToListRecursiveCond when this PR lands: # https://github.com/NixOS/nixpkgs/pull/395160 - justAttrNames = + listAttrs = path: value: let result = if path == [ "AAAAAASomeThingsFailToEvaluate" ] || !(lib.isAttrs value) then [ ] else if lib.isDerivation value then - [ path ] + [ + { + inherit path value; + } + ] else lib.pipe value [ (lib.mapAttrsToList ( name: value: lib.addErrorContext "while evaluating package set attribute path '${ lib.showAttrPath (path ++ [ name ]) - }'" (justAttrNames (path ++ [ name ]) value) + }'" (listAttrs (path ++ [ name ]) value) )) lib.concatLists ]; @@ -50,39 +55,74 @@ let attrNamesOnly = true; }; - paths = [ - # Some of the following are based on variants, which are disabled with `attrNamesOnly = true`. - # Until these have been removed from release.nix / hydra, we manually add them to the list. - [ - "pkgsLLVM" - "stdenv" - ] - [ - "pkgsArocc" - "stdenv" - ] - [ - "pkgsZig" - "stdenv" - ] - [ - "pkgsStatic" - "stdenv" - ] - [ - "pkgsMusl" - "stdenv" - ] - ] - ++ justAttrNames [ ] outpaths; - + list = + map + (path: { + inherit path; + # This looks a bit weird, but the only reason we care about this value + # is for the meta.problems check below, and stdenv's certainly don't + # have any problems, so this is fine :) + value = { }; + }) + [ + # Some of the following are based on variants, which are disabled with `attrNamesOnly = true`. + # Until these have been removed from release.nix / hydra, we manually add them to the list. + [ + "pkgsLLVM" + "stdenv" + ] + [ + "pkgsArocc" + "stdenv" + ] + [ + "pkgsZig" + "stdenv" + ] + [ + "pkgsStatic" + "stdenv" + ] + [ + "pkgsMusl" + "stdenv" + ] + ] + ++ listAttrs [ ] outpaths; + paths = map (attrs: attrs.path) list; names = map lib.showAttrPath paths; + inherit (import ../../pkgs/stdenv/generic/problems.nix { inherit lib; }) + disallowNixpkgsInternalUseKinds + ; + + # Determine the list of attributes whose packages have any meta.problems + # with a kind that's disallowed from internal Nixpkgs use + attrPathsDisallowedForInternalUse = lib.pipe list [ + (lib.map ( + attrs: + attrs + // { + problematicProblems = builtins.tryEval ( + lib.filterAttrs (name: problem: disallowNixpkgsInternalUseKinds ? ${problem.kind}) ( + attrs.value.meta.problems or { } + ) + ); + } + )) + (lib.filter (attrs: attrs.problematicProblems.success && attrs.problematicProblems.value != { })) + (lib.map (attrs: { + attrPath = attrs.path; + reason = "it has certain meta.problems whose kinds are disallowed: ${ + lib.generators.toPretty { } attrs.problematicProblems.value + }"; + })) + ]; in { # TODO: Do we still need these? Probably not inherit paths names; result = { - inherit paths; + inherit paths attrPathsDisallowedForInternalUse; }; } diff --git a/pkgs/stdenv/generic/problems.nix b/pkgs/stdenv/generic/problems.nix index c7b6754d729d5..e21f2126acbe4 100644 --- a/pkgs/stdenv/generic/problems.nix +++ b/pkgs/stdenv/generic/problems.nix @@ -76,6 +76,7 @@ rec { maintainerless = { manualAllowed = false; isUnique = false; + nixpkgsInternalUseAllowed = true; automatic = { condition = # To get usable output, we want to avoid flagging "internal" derivations. @@ -98,6 +99,7 @@ rec { broken = { manualAllowed = true; isUnique = false; + nixpkgsInternalUseAllowed = true; automatic = { condition = config: @@ -122,11 +124,13 @@ rec { removal = { manualAllowed = true; isUnique = true; + nixpkgsInternalUseAllowed = false; automatic = null; }; deprecated = { manualAllowed = true; isUnique = false; + nixpkgsInternalUseAllowed = false; automatic = null; }; }; @@ -136,6 +140,10 @@ rec { # Problem kinds that are currently only allowed to be specified once uniqueKinds = lib.filterAttrs (name: value: value.isUnique) kinds; + disallowNixpkgsInternalUseKinds = lib.filterAttrs ( + name: value: !value.nixpkgsInternalUseAllowed + ) kinds; + automaticProblems = lib.mapAttrsToList (name: value: value.automatic // { kindName = name; }) ( lib.filterAttrs (name: value: value.automatic != null) kinds ); diff --git a/pkgs/top-level/default.nix b/pkgs/top-level/default.nix index 4d44abadd961b..4e19941441daf 100644 --- a/pkgs/top-level/default.nix +++ b/pkgs/top-level/default.nix @@ -217,6 +217,7 @@ let fixedPoint = boot stages; pkgs = + # Generally only set by CI, don't want to cause a performance hit for users if config.attrPathsDisallowedForInternalUse == [ ] then fixedPoint else diff --git a/pkgs/top-level/stage.nix b/pkgs/top-level/stage.nix index 35929783e7c95..bc50a3f24247e 100644 --- a/pkgs/top-level/stage.nix +++ b/pkgs/top-level/stage.nix @@ -314,6 +314,7 @@ let # See also ./default.nix, where these attributes are added back again so they're still checked by CI internallyDisallowedAttrPathsOverlay = final: prev: + # Generally only set by CI, don't want to cause a performance hit for users if config.attrPathsDisallowedForInternalUse == [ ] then { } else From 8fa1b2e9795b14da56354bee48a0679a4fa15b28 Mon Sep 17 00:00:00 2001 From: Silvan Mosberger Date: Tue, 23 Jun 2026 21:07:44 +0200 Subject: [PATCH 6/6] release-checks: Prevent meta.problem warnings CI already catches problematic uses, we don't need to bother about them here --- pkgs/top-level/nixpkgs-basic-release-checks.nix | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkgs/top-level/nixpkgs-basic-release-checks.nix b/pkgs/top-level/nixpkgs-basic-release-checks.nix index 1c7e606559857..e10d6f5e1ed24 100644 --- a/pkgs/top-level/nixpkgs-basic-release-checks.nix +++ b/pkgs/top-level/nixpkgs-basic-release-checks.nix @@ -56,7 +56,7 @@ pkgs.runCommand "nixpkgs-release-checks" set -x nix-env -f $src \ --show-trace --argstr system "$platform" \ - --arg config '{ allowAliases = false; allowDeprecatedx86_64Darwin = true; }' \ + --arg config '{ lib, pkgs }: { problems.matchers = lib.mkForce [ ]; allowAliases = false; allowDeprecatedx86_64Darwin = true; }' \ --option lint-url-literals fatal \ -qa --drv-path --system-filter \* --system \ "''${opts[@]}" 2> eval-warnings.log > packages1 @@ -72,7 +72,7 @@ pkgs.runCommand "nixpkgs-release-checks" nix-env -f $src2 \ --show-trace --argstr system "$platform" \ - --arg config '{ allowAliases = false; allowDeprecatedx86_64Darwin = true; }' \ + --arg config '{ lib, pkgs }: { problems.matchers = lib.mkForce [ ]; allowAliases = false; allowDeprecatedx86_64Darwin = true; }' \ --option lint-url-literals fatal \ -qa --drv-path --system-filter \* --system \ "''${opts[@]}" > packages2 @@ -95,7 +95,7 @@ pkgs.runCommand "nixpkgs-release-checks" nix-env -f $src \ --show-trace --argstr system "$platform" \ - --arg config '{ allowAliases = false; allowDeprecatedx86_64Darwin = true; }' \ + --arg config '{ lib, pkgs }: { problems.matchers = lib.mkForce [ ]; allowAliases = false; allowDeprecatedx86_64Darwin = true; }' \ --option lint-url-literals fatal \ -qa --drv-path --system-filter \* --system --meta --xml \ "''${opts[@]}" > /dev/null