diff --git a/nixos/lib/testing/driver.nix b/nixos/lib/testing/driver.nix index 69392eb052af..f740dd3cbdf6 100644 --- a/nixos/lib/testing/driver.nix +++ b/nixos/lib/testing/driver.nix @@ -5,7 +5,12 @@ ... }: let - inherit (lib) mkOption types literalMD; + inherit (lib) + mkOption + types + literalExpression + literalMD + ; inherit (config) sshBackdoor; @@ -117,9 +122,10 @@ in { options = { pythonTestDriverPackage = mkOption { - description = "Package containing the python NixOS test driver implemetnation"; + description = "Package containing the python NixOS test driver implementation"; type = types.package; default = hostPkgs.nixos-test-driver; + defaultText = literalExpression "hostPkgs.nixos-test-driver"; readOnly = true; }; diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index f9ecf72845ed..9647bcdbf0f5 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -153,6 +153,7 @@ in console-log = runTest ./nixos-test-driver/console-log.nix; containers = runTest ./nixos-test-driver/containers.nix; skip-typecheck = runTest ./nixos-test-driver/skip-typecheck.nix; + options-doc-regression = import ./nixos-test-driver/options-doc-regression.nix { inherit pkgs; }; driver-timeout = pkgs.runCommand "ensure-timeout-induced-failure" { diff --git a/nixos/tests/nixos-test-driver/options-doc-regression.nix b/nixos/tests/nixos-test-driver/options-doc-regression.nix new file mode 100644 index 000000000000..72ef8abe863d --- /dev/null +++ b/nixos/tests/nixos-test-driver/options-doc-regression.nix @@ -0,0 +1,58 @@ +# Regression test for the `pythonTestDriverPackage` option's default value +# leaking a `hostPkgs` reference into the NixOS manual build. +# +# `pythonTestDriverPackage` (added in d95261b435c4, "nixos-test-driver: Make +# overridable") uses `default = hostPkgs.nixos-test-driver`. Without a +# `defaultText`, the options-doc renderer force-evaluates that default when +# building `options.json` for the NixOS manual. `hostPkgs` is only defined in +# the VM testing framework, so evaluating its default from a regular NixOS +# system configuration throws: +# +# error: The option `hostPkgs' was accessed but has no value defined. +# +# In practice the bug only surfaces when `pkgs` ends up depending on +# `config` — e.g. when `nixpkgs.config.packageOverrides` is wrapped in +# `lib.mkIf`. Otherwise `nixos/doc/manual/default.nix`'s fallback +# `config.hostPkgs = pkgs` rescue holds. With the dependency, that rescue +# creates a cycle and the original `hostPkgs` error surfaces. +# +# The test builds a minimal system whose `pkgs` depends on `config`, and +# asserts the toplevel (which includes `nixos-manual-html`) evaluates. + +{ + pkgs, + ... +}: +let + evalConfig = import ../../lib/eval-config.nix; + + nixos = evalConfig { + system = null; + modules = [ + ( + { lib, ... }: + { + system.stateVersion = "25.05"; + fileSystems."/" = { + device = "/dev/null"; + fsType = "none"; + }; + boot.loader.grub.device = "nodev"; + nixpkgs.hostPlatform = pkgs.stdenv.hostPlatform.system; + + # This is the trigger: wrapping `packageOverrides` in `mkIf` makes + # the `pkgs` module argument depend on `config`, which defeats the + # `config.hostPkgs = pkgs` rescue in `nixos/doc/manual/default.nix`. + nixpkgs.config.packageOverrides = lib.mkIf false (_: { }); + } + ) + ]; + }; +in +pkgs.runCommand "nixos-test-driver-options-doc-regression" + { + toplevel = nixos.config.system.build.toplevel.drvPath; + } + '' + echo "$toplevel" > $out + ''