Reapply "nixos/nixpkgs.config: make use of the module defined in config.nix"

This reverts commit a06a6caa6b.
This commit is contained in:
Johannes Kirschbauer
2026-05-07 19:11:47 +02:00
parent 3c76fb7d83
commit 86cd4e7bc8
5 changed files with 203 additions and 62 deletions

View File

@@ -2,51 +2,12 @@
config,
options,
lib,
pkgs,
...
}:
let
cfg = config.nixpkgs;
opt = options.nixpkgs;
isConfig = x: builtins.isAttrs x || lib.isFunction x;
optCall = f: x: if lib.isFunction f then f x else f;
mergeConfig =
lhs_: rhs_:
let
lhs = optCall lhs_ { inherit lib pkgs; };
rhs = optCall rhs_ { inherit lib pkgs; };
in
lib.recursiveUpdate lhs rhs
// lib.optionalAttrs (lhs ? allowUnfreePackages) {
allowUnfreePackages = lhs.allowUnfreePackages ++ (lib.attrByPath [ "allowUnfreePackages" ] [ ] rhs);
}
// lib.optionalAttrs (lhs ? packageOverrides) {
packageOverrides =
pkgs:
optCall lhs.packageOverrides pkgs // optCall (lib.attrByPath [ "packageOverrides" ] { } rhs) pkgs;
}
// lib.optionalAttrs (lhs ? perlPackageOverrides) {
perlPackageOverrides =
pkgs:
optCall lhs.perlPackageOverrides pkgs
// optCall (lib.attrByPath [ "perlPackageOverrides" ] { } rhs) pkgs;
};
configType = lib.mkOptionType {
name = "nixpkgs-config";
description = "nixpkgs config";
check =
x:
let
traceXIfNot = c: if c x then true else lib.traceSeqN 1 x false;
in
traceXIfNot isConfig;
merge = args: lib.foldr (def: mergeConfig def.value) { };
};
overlayType = lib.mkOptionType {
name = "nixpkgs-overlay";
description = "nixpkgs overlay";
@@ -73,6 +34,8 @@ let
++ lib.optional (opt.localSystem.highestPrio < (lib.mkOptionDefault { }).priority) opt.localSystem
++ lib.optional (opt.crossSystem.highestPrio < (lib.mkOptionDefault { }).priority) opt.crossSystem;
_configDefinitions = opt.config.definitionsWithLocations;
defaultPkgs =
if opt.hostPlatform.isDefined then
let
@@ -90,14 +53,15 @@ let
in
import ../../.. (
{
inherit (cfg) config overlays;
inherit _configDefinitions;
inherit (cfg) overlays;
}
// systemArgs
)
else
import ../../.. {
inherit _configDefinitions;
inherit (cfg)
config
overlays
localSystem
crossSystem
@@ -165,7 +129,15 @@ in
example = lib.literalExpression ''
{ allowBroken = true; allowUnfree = true; }
'';
type = configType;
type = lib.types.deferredModuleWith {
staticModules = [
{ _module.args.docPrefix = "https://nixos.org/manual/nixpkgs/unstable/"; }
../../../pkgs/top-level/config.nix
];
};
# Returns pkgs.config instead of nixpkgs.config
# This shadows the deferredModule to make it look like a submodule
apply = _: finalPkgs.config;
description = ''
Global configuration for Nixpkgs.
The complete list of [Nixpkgs configuration options](https://nixos.org/manual/nixpkgs/unstable/#sec-config-options-reference) is in the [Nixpkgs manual section on global configuration](https://nixos.org/manual/nixpkgs/unstable/#chap-packageconfig).
@@ -406,7 +378,7 @@ in
'';
}
{
assertion = opt.pkgs.isDefined -> cfg.config == { };
assertion = opt.pkgs.isDefined -> opt.config.highestPrio == (lib.mkOptionDefault null).priority;
message = ''
Your system configures nixpkgs with an externally created instance.
`nixpkgs.config` options should be passed when creating the instance instead.

View File

@@ -0,0 +1,117 @@
# Tests for nixpkgs config forwarding from NixOS modules.
#
# Run with:
# nix-unit pkgs/test/config-nix-unit.nix
# or
# nix-build -A tests.config-nix-unit
#
{
nixpkgsPath ? ../..,
pkgs ? import nixpkgsPath { },
}:
let
lib = pkgs.lib;
# Test helper
evalNixos =
modules:
import (nixpkgsPath + "/nixos/lib/eval-config.nix") {
modules = [ { nixpkgs.hostPlatform = "x86_64-linux"; } ] ++ modules;
};
in
{
# Basic: a single config option is forwarded correctly.
testSingleConfigOption = {
expr = (evalNixos [ { nixpkgs.config.allowUnfree = true; } ]).config.nixpkgs.config.allowUnfree;
expected = true;
};
# Multiple config definitions from separate modules are merged.
testMultipleModulesMerge = {
expr =
let
eval = evalNixos [
{ nixpkgs.config.allowUnfree = true; }
{ nixpkgs.config.allowBroken = true; }
];
in
{
inherit (eval.config.nixpkgs.config) allowUnfree allowBroken;
};
expected = {
allowUnfree = true;
allowBroken = true;
};
};
# mkForce works. Also covers other properties
testMkForce = {
expr =
(evalNixos [
{ nixpkgs.config.allowUnfree = true; }
{ nixpkgs.config.allowUnfree = lib.mkForce false; }
]).config.nixpkgs.config.allowUnfree;
expected = false;
};
testDefaults = {
expr = (evalNixos [ ]).config.nixpkgs.config.allowUnfree;
expected = false;
};
# Standalone nixpkgs (i.e. import <nixpkgs> { ... })
testStandaloneConfig = {
expr = (import nixpkgsPath { config.allowUnfree = true; }).config.allowUnfree;
expected = true;
};
# Standalone nixpkgs with a function (i.e. import <nixpkgs> ({pkgs, lib, ...}: { ... })
testStandaloneConfigFunctionPkgs = {
expr =
(import nixpkgsPath {
config =
{ pkgs, lib, ... }:
{
allowUnfree = lib.isAttrs pkgs;
};
}).config.allowUnfree;
expected = true;
};
# NixOS module sets nixpkgs.config as a function
testNixosConfigFunction = {
expr =
(evalNixos [
{
nixpkgs.config =
{ lib, ... }:
{
allowUnfree = lib.isFunction lib.id;
};
}
]).config.nixpkgs.config.allowUnfree;
expected = true;
};
# Passing both config and _configDefinitions is not allowed
testConfigAndDefinitionsMutuallyExclusive = {
expr =
(import nixpkgsPath {
config = {
allowUnfree = true;
};
_configDefinitions = [
{
file = "test";
value = {
allowBroken = true;
};
}
];
}).config.allowUnfree;
expectedError = {
type = "ThrownError";
msg = ".*_configDefinitions.*internal.*must not be combined.*";
};
};
}

View File

@@ -128,6 +128,21 @@ in
config = callPackage ./config.nix { };
# Technically nix-unit binds to a fixed nix version
# We have tests in lib to test the module system itself against different nix-versions
# Based on this assumption (transitivity of correctness) this test should therefore also cover all tested nix-versions
config-nix-unit =
pkgs.runCommand "config-nix-unit"
{
nativeBuildInputs = [ pkgs.nix-unit ];
}
''
export HOME=$TMPDIR
nix-unit --eval-store "$HOME" ${./config-nix-unit.nix} \
--arg nixpkgsPath "${../..}"
mkdir $out
'';
top-level = callPackage ./top-level { };
haskell = callPackage ./haskell { };

View File

@@ -6,7 +6,12 @@
# nix-build -A tests.config
#
{ config, lib, ... }:
{
config,
lib,
docPrefix,
...
}:
let
inherit (lib)
@@ -115,13 +120,13 @@ let
gitConfig = mkOption {
type = types.attrsOf (types.attrsOf types.anything);
description = ''
The default [git configuration](https://git-scm.com/docs/git-config#_variables) for all [`pkgs.fetchgit`](#fetchgit) calls.
The default [git configuration](https://git-scm.com/docs/git-config#_variables) for all [`pkgs.fetchgit`](${docPrefix}#fetchgit) calls.
Among many other potential uses, this can be used to override URLs to point to local mirrors.
Changing this will not cause any rebuilds because `pkgs.fetchgit` produces a [fixed-output derivation](https://nix.dev/manual/nix/stable/glossary.html?highlight=fixed-output%20derivation#gloss-fixed-output-derivation).
To set the configuration file directly, use the [`gitConfigFile`](#opt-gitConfigFile) option instead.
To set the configuration file directly, use the [`gitConfigFile`](${docPrefix}#opt-gitConfigFile) option instead.
To set the configuration file for individual calls, use `fetchgit { gitConfigFile = "..."; }`.
'';
@@ -135,9 +140,9 @@ let
gitConfigFile = mkOption {
type = types.nullOr types.path;
description = ''
A path to a [git configuration](https://git-scm.com/docs/git-config#_variables) file, to be used for all [`pkgs.fetchgit`](#fetchgit) calls.
A path to a [git configuration](https://git-scm.com/docs/git-config#_variables) file, to be used for all [`pkgs.fetchgit`](${docPrefix}#fetchgit) calls.
This overrides the [`gitConfig`](#opt-gitConfig) option, see its documentation for more details.
This overrides the [`gitConfig`](${docPrefix}#opt-gitConfig) option, see its documentation for more details.
'';
default =
if config.gitConfig != { } then
@@ -155,7 +160,7 @@ let
For example, an override like `"registry.npmjs.org" = "my-mirror.local/registry.npmjs.org"` will replace a URL like `https://registry.npmjs.org/foo.tar.gz` with `https://my-mirror.local/registry.npmjs.org/foo.tar.gz`.
To set the string directly, see [`npmRegistryOverridesString`](#opt-npmRegistryOverridesString).
To set the string directly, see [`npmRegistryOverridesString`](${docPrefix}#opt-npmRegistryOverridesString).
'';
default = { };
example = {
@@ -174,7 +179,7 @@ let
description = ''
A string containing a string with a JSON representation of npm registry overrides for `fetchNpmDeps`.
This overrides the [`npmRegistryOverrides`](#opt-npmRegistryOverrides) option, see its documentation for more details.
This overrides the [`npmRegistryOverrides`](${docPrefix}#opt-npmRegistryOverrides) option, see its documentation for more details.
'';
default = builtins.toJSON config.npmRegistryOverrides;
};
@@ -412,7 +417,7 @@ let
type = types.listOf types.str;
default = [ "https://tarballs.nixos.org" ];
description = ''
The set of content-addressed/hashed mirror URLs used by [`pkgs.fetchurl`](#sec-pkgs-fetchers-fetchurl).
The set of content-addressed/hashed mirror URLs used by [`pkgs.fetchurl`](${docPrefix}#sec-pkgs-fetchers-fetchurl).
In case `pkgs.fetchurl` can't download from the given URLs,
it will try the hashed mirrors based on the expected output hash.
@@ -466,11 +471,27 @@ let
Silence the warning for the upcoming deprecation of the
`x86_64-darwin` platform in Nixpkgs 26.11.
See the [release notes](#x86_64-darwin-26.05) for more
See the [release notes](${docPrefix}#x86_64-darwin-26.05) for more
information.
'';
};
packageOverrides = mkOption {
type = types.functionTo types.attrs;
default = pkgs: { };
description = ''
A function to replace or add packages in `pkgs` expects an attrset to be returned when called.
'';
};
perlPackageOverrides = mkOption {
type = types.functionTo types.attrs;
default = pkgs: { };
description = ''
The same as `packageOverrides` but for packages in the perl package set.
'';
};
problems = (import ../stdenv/generic/problems.nix { inherit lib; }).configOptions;
};
@@ -494,6 +515,7 @@ in
inherit options;
config = {
_module.args.docPrefix = lib.mkDefault "";
warnings =
optionals config.warnUndeclaredOptions (
mapAttrsToList (k: v: "undeclared Nixpkgs option set: config.${k}") config._undeclared or { }

View File

@@ -64,6 +64,11 @@ in
# list it returns.
stdenvStages ? import ../stdenv,
# Temporary parameter to unify nixpkgs/pkgs evaluation
# Internal, do not use this manually!
# Will be removed again within the next releases
_configDefinitions ? null,
# Ignore unexpected args.
...
}@args:
@@ -109,7 +114,13 @@ let
then
x86_64DarwinDeprecationWarning
else
x: x
x:
x throwIfNot (lib.all lib.isFunction crossOverlays)
"All crossOverlays passed to nixpkgs must be functions."
)
(
throwIfNot (_configDefinitions == null || config0 == { })
"The `_configDefinitions` argument is an internal interface and must not be combined with `config`."
);
localSystem = lib.systems.elaborate args.localSystem;
@@ -134,20 +145,24 @@ let
# Allow both:
# { /* the config */ } and
# { pkgs, ... } : { /* the config */ }
# { lib, pkgs, ... } : { /* the config */ }
config1 = if lib.isFunction config0 then config0 { inherit lib pkgs; } else config0;
configEval = lib.evalModules {
modules = [
./config.nix
(
{ options, ... }:
{
_file = "nixpkgs.config";
config = config1;
}
)
];
]
++ (
if _configDefinitions != null then
map (def: lib.modules.setDefaultModuleLocation def.file def.value) _configDefinitions
else
[
{
_file = "nixpkgs.config";
config = config1;
}
]
);
class = "nixpkgsConfig";
};