diff --git a/flake-modules/tests.nix b/flake-modules/tests.nix index 674edbeb..bf80e78c 100644 --- a/flake-modules/tests.nix +++ b/flake-modules/tests.nix @@ -9,6 +9,9 @@ makeNixvimWithModule, ... }: + let + evaluatedNixvim = helpers.modules.evalNixvim { check = false; }; + in { checks = { extra-args-tests = import ../tests/extra-args.nix { @@ -43,6 +46,8 @@ maintainers = import ../tests/maintainers.nix { inherit pkgs; }; generated = pkgs.callPackage ../tests/generated.nix { }; + + package-options = pkgs.callPackage ../tests/package-options.nix { inherit evaluatedNixvim; }; } // import ../tests { inherit pkgs pkgsUnfree helpers; }; }; } diff --git a/tests/package-options.nix b/tests/package-options.nix new file mode 100644 index 00000000..d6fc3642 --- /dev/null +++ b/tests/package-options.nix @@ -0,0 +1,90 @@ +# This test ensures "package" options use a "literalExpression" in their defaultText +# I.e. it validates `lib.mkPackageOption` was used to build the package options. +{ + evaluatedNixvim, + lib, + runCommandNoCCLocal, +}: +let + inherit (builtins) + filter + head + map + match + ; + inherit (lib.strings) concatMapStringsSep removePrefix removeSuffix; + inherit (lib.attrsets) isDerivation; + inherit (lib.options) + isOption + renderOptionValue + showOption + ; + + # This doesn't collect any submodule sub-options, + # but that's fine since most of our "package" options are in the top level module eval + options = lib.collect isOption evaluatedNixvim.options; + + # All visible non-sub options that default to a derivation + drvOptions = filter ( + { + default ? null, + visible ? true, + internal ? false, + ... + }: + visible && !internal && isDerivation default + ) options; + + # Bad options do not use `literalExpression` in their `defaultText`, + # or have a `defaultText` that doesn't start with "pkgs." + badOptions = filter ( + opt: + opt.defaultText._type or null != "literalExpression" + || match ''pkgs[.].*'' (opt.defaultText.text or "") == null + ) drvOptions; +in +runCommandNoCCLocal "validate-package-options" + { + # Use structuredAttrs to avoid "Argument List Too Long" errors + # and get proper bash array support. + __structuredAttrs = true; + + # Passthroughs for debugging purposes + passthru = { + inherit evaluatedNixvim drvOptions badOptions; + }; + + # Error strings to print + errors = + let + # A little hack to get the flake's source in the nix store + # We will use this to make the option declaration sources more readable + src = removeSuffix "modules/top-level/output.nix" ( + head evaluatedNixvim.options.package.declarations + ); + in + map ( + opt: + let + # The default, as rendered in documentation. Will always be a literalExpression. + default = builtins.addErrorContext "while evaluating the default text for `${showOption opt.loc}`" ( + renderOptionValue (opt.defaultText or opt.default) + ); + in + '' + - ${showOption opt.loc} (${default.text}), declared in: + ${concatMapStringsSep "\n" (file: " - ${removePrefix src file}") opt.declarations} + '' + ) badOptions; + } + '' + if [ -n "$errors" ]; then + echo "Found ''${#errors[@]} errors:" + for err in "''${errors[@]}"; do + echo "$err" + done + echo "(''${#errors[@]} options with errors)" + exit 1 + fi + touch $out + ''