diff --git a/nixos/modules/misc/nixpkgs/test-nixpkgs-config-allow-unfree.nix b/nixos/modules/misc/nixpkgs/test-nixpkgs-config-allow-unfree.nix new file mode 100644 index 000000000000..201a43314fbf --- /dev/null +++ b/nixos/modules/misc/nixpkgs/test-nixpkgs-config-allow-unfree.nix @@ -0,0 +1,124 @@ +# Test for allowUnfreePackages merging across multiple modules +# Run with: nix-build -A nixosTests.nixpkgs-config-allow-unfree --show-trace + +{ + evalMinimalConfig, + lib, +}: +let + eval = + modules: + evalMinimalConfig { + imports = [ + ../nixpkgs.nix + ] + ++ modules; + }; + + getConfig = evalResult: evalResult.config.nixpkgs.config; + + sortList = list: builtins.sort builtins.lessThan list; + + assertEquals = + expected: actual: + let + prettyExpected = lib.generators.toPretty { } expected; + prettyActual = lib.generators.toPretty { } actual; + in + lib.assertMsg (expected == actual) "Expected ${prettyExpected} but got ${prettyActual}"; + + assertUnfreePackages = + listOfModules: expectedPackages: + let + config = getConfig (eval listOfModules); + actualAllowedUnfreePackages = sortList config.allowUnfreePackages; + in + assertEquals expectedPackages actualAllowedUnfreePackages; +in +lib.recurseIntoAttrs { + + singleModuleTest = + assertUnfreePackages + [ + { + nixpkgs.config.allowUnfreePackages = [ + "package1" + "package2" + ]; + } + ] + [ + "package1" + "package2" + ]; + + multipleModulesMerging = + assertUnfreePackages + [ + { + _file = "module1.nix"; + nixpkgs.config.allowUnfreePackages = [ + "package1" + "package2" + ]; + } + { + _file = "module2.nix"; + nixpkgs.config.allowUnfreePackages = [ + "package3" + "package4" + ]; + } + { + _file = "module3.nix"; + nixpkgs.config.allowUnfreePackages = [ "package5" ]; + } + ] + [ + "package1" + "package2" + "package3" + "package4" + "package5" + ]; + + overlappingPackagesMerging = + assertUnfreePackages + [ + { + _file = "moduleA.nix"; + nixpkgs.config.allowUnfreePackages = [ + "shared-package" + "unique-a" + ]; + } + { + _file = "moduleB.nix"; + nixpkgs.config.allowUnfreePackages = [ + "shared-package" + "unique-b" + ]; + } + ] + [ + "shared-package" + "shared-package" + "unique-a" + "unique-b" + ]; + + emptyListMerging = + assertUnfreePackages + [ + { + _file = "empty.nix"; + nixpkgs.config.allowUnfreePackages = [ ]; + } + { + _file = "non-empty.nix"; + nixpkgs.config.allowUnfreePackages = [ "some-package" ]; + } + ] + [ "some-package" ]; + +} diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index bbb72047c109..42582b9ed737 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -1103,6 +1103,13 @@ in imports = [ ./nixos-rebuild-target-host.nix ]; }; nixpkgs = pkgs.callPackage ../modules/misc/nixpkgs/test.nix { inherit evalMinimalConfig; }; + nixpkgs-config-allow-unfree = + pkgs.callPackage ../modules/misc/nixpkgs/test-nixpkgs-config-allow-unfree.nix + { inherit evalMinimalConfig; }; + nixpkgs-config-allow-unfree-packages-and-predicate = + pkgs.callPackage ../../pkgs/stdenv/generic/check-meta-test.nix + { }; + nixseparatedebuginfod = runTest ./nixseparatedebuginfod.nix; nixseparatedebuginfod2 = runTest ./nixseparatedebuginfod2.nix; node-red = runTest ./node-red.nix; nomad = runTest ./nomad.nix; diff --git a/pkgs/stdenv/generic/check-meta-test.nix b/pkgs/stdenv/generic/check-meta-test.nix new file mode 100644 index 000000000000..0dedb25afbd2 --- /dev/null +++ b/pkgs/stdenv/generic/check-meta-test.nix @@ -0,0 +1,128 @@ +# Execute with +# nix-build -A nixosTests.nixpkgs-config-allow-unfree-packages-and-predicate --show-trace +# +# This test exercises the interaction between: +# +# - nixos/modules/misc/nixpkgs.nix (config merging, esp. allowUnfreePackages) +# - pkgs/stdenv/generic/check-meta.nix (allowUnfreePredicate logic) +# +# It checks how: +# +# * config.allowUnfreePackages +# * config.allowUnfreePredicate +# +# interact to determine whether unfree packages are allowed. +{ + lib, + pkgs, +}: + +let + inherit (lib) + assertMsg + generators + licenses + recurseIntoAttrs + ; + + mkUnfreePkg = name: { + pname = name; + version = "1.0"; + meta.license = licenses.unfree; + }; + assertValidity = + { + nixpkgsConfig, + pkg, + expected ? true, + }: + let + testPkgs = import ../../.. { + system = pkgs.stdenv.hostPlatform.system; + config = nixpkgsConfig; + }; + checkMeta = testPkgs.callPackage ./check-meta.nix { }; + tryEval = expression: builtins.tryEval (builtins.deepSeq expression expression); + actual = tryEval ( + checkMeta.assertValidity { + meta = pkg.meta; + attrs = pkg; + } + ); + toPretty = generators.toPretty { }; + in + assertMsg (actual.success == expected) '' + Expected validity of package ${lib.getName pkg} to be ${toPretty expected}, + but got ${toPretty actual} with config: + ${toPretty nixpkgsConfig} + ''; + + runAssertions = assertions: lib.deepSeq assertions ""; + +in +recurseIntoAttrs { + + allowOnlyFreePackagesByDefault = assertValidity { + nixpkgsConfig = { }; + pkg = mkUnfreePkg "forbidden"; + expected = false; + }; + + allowAllUnfreePackages = assertValidity { + nixpkgsConfig = { + allowUnfree = true; + }; + pkg = mkUnfreePkg "allowed"; + }; + + allowUnfreePackagesWithPredicate = + let + nixpkgsConfig = { + allowUnfreePredicate = pkg: lib.getName pkg == "allowed-by-predicate"; + }; + in + [ + (assertValidity { + inherit nixpkgsConfig; + pkg = mkUnfreePkg "allowed-by-predicate"; + }) + (assertValidity { + inherit nixpkgsConfig; + pkg = mkUnfreePkg "allowed-by-nothing"; + expected = false; + }) + ]; + + allowUnfreeWithPackages = runAssertions [ + (assertValidity { + nixpkgsConfig = { + allowUnfreePackages = [ "unfree" ]; + }; + pkg = mkUnfreePkg "unfree"; + expected = true; + }) + ]; + + allowUnfreePackagesOrPredicate = + let + nixpkgsConfig = { + allowUnfreePackages = [ "allowed-by-packages" ]; + allowUnfreePredicate = pkg: lib.getName pkg == "allowed-by-predicate"; + }; + in + runAssertions [ + (assertValidity { + inherit nixpkgsConfig; + pkg = mkUnfreePkg "allowed-by-packages"; + }) + (assertValidity { + inherit nixpkgsConfig; + pkg = mkUnfreePkg "allowed-by-predicate"; + }) + (assertValidity { + inherit nixpkgsConfig; + pkg = mkUnfreePkg "forbidden"; + expected = false; + }) + ]; +} diff --git a/pkgs/stdenv/generic/check-meta.nix b/pkgs/stdenv/generic/check-meta.nix index 688429685863..ae868ab14ac1 100644 --- a/pkgs/stdenv/generic/check-meta.nix +++ b/pkgs/stdenv/generic/check-meta.nix @@ -160,8 +160,17 @@ let # } # Defaults to allow all names defined in config.allowUnfreePackages allowUnfreePredicate = - config.allowUnfreePredicate - or (pkg: builtins.elem (lib.getName pkg) config.allowUnfreePackages or [ ]); + let + listPredicate = pkg: builtins.elem (lib.getName pkg) (config.allowUnfreePackages or [ ]); + + # Be robust against misconfigured allowUnfreePredicate values such as null + explicitPredicate = + let + raw = config.allowUnfreePredicate or null; + in + if builtins.isFunction raw then raw else (_: false); + in + pkg: (listPredicate pkg) || (explicitPredicate pkg); # Check whether unfree packages are allowed and if not, whether the # package has an unfree license and is not explicitly allowed by the