push sheeet
Some checks failed
Periodic Merges (6h) / master → staging-nixos (push) Failing after 12m50s
Periodic Merges (6h) / master → staging-next (push) Failing after 12m54s
Periodic Merges (24h) / merge-base(master,staging) → haskell-updates (push) Failing after 11m54s
Periodic Merges (6h) / staging-next → staging (push) Failing after 12m13s
Periodic Merges (24h) / staging-next-25.05 → staging-25.05 (push) Failing after 13m24s
Periodic Merges (24h) / release-25.05 → staging-next-25.05 (push) Failing after 14m28s

This commit is contained in:
Dark Steveneq
2025-10-09 14:15:47 +02:00
commit 646b892680
49168 changed files with 5897842 additions and 0 deletions

3
lib/tests/.editorconfig Normal file
View File

@@ -0,0 +1,3 @@
[*.plist]
indent_style = tab
insert_final_newline = unset

View File

@@ -0,0 +1,379 @@
{
pkgs ? import ../.. { },
currLibPath ? ../.,
prevLibPath ? "${
pkgs.fetchFromGitHub {
owner = "nixos";
repo = "nixpkgs";
# Parent commit of [#391544](https://github.com/NixOS/nixpkgs/pull/391544)
# Which was before the type.merge.v2 introduction
rev = "bcf94dd3f07189b7475d823c8d67d08b58289905";
hash = "sha256-MuMiIY3MX5pFSOCvutmmRhV6RD0R3CG0Hmazkg8cMFI=";
}
}/lib",
}:
let
lib = import currLibPath;
lib_with_merge_v2 = lib;
lib_with_merge_v1 = import prevLibPath;
getMatrix =
{
getType ? null,
# If getType is set this is only used as test prefix
# And the type from getType is used
outerTypeName,
innerTypeName,
value,
testAttrs,
}:
let
evalModules.call_v1 = lib_with_merge_v1.evalModules;
evalModules.call_v2 = lib_with_merge_v2.evalModules;
outerTypes.outer_v1 = lib_with_merge_v1.types;
outerTypes.outer_v2 = lib_with_merge_v2.types;
innerTypes.inner_v1 = lib_with_merge_v1.types;
innerTypes.inner_v2 = lib_with_merge_v2.types;
in
lib.mapAttrs (
_: evalModules:
lib.mapAttrs (
_: outerTypes:
lib.mapAttrs (_: innerTypes: {
"test_${outerTypeName}_${innerTypeName}" = testAttrs // {
expr =
(evalModules {
modules = [
(m: {
options.foo = m.lib.mkOption {
type =
if getType != null then
getType outerTypes innerTypes
else
outerTypes.${outerTypeName} innerTypes.${innerTypeName};
default = value;
};
})
];
}).config.foo;
};
}) innerTypes
) outerTypes
) evalModules;
in
{
# AttrsOf string
attrsOf_str_ok = getMatrix {
outerTypeName = "attrsOf";
innerTypeName = "str";
value = {
bar = "test";
};
testAttrs = {
expected = {
bar = "test";
};
};
};
attrsOf_str_err_inner = getMatrix {
outerTypeName = "attrsOf";
innerTypeName = "str";
value = {
bar = 1; # not a string
};
testAttrs = {
expectedError = {
type = "ThrownError";
msg = "A definition for option `foo.bar' is not of type `string'.*";
};
};
};
attrsOf_str_err_outer = getMatrix {
outerTypeName = "attrsOf";
innerTypeName = "str";
value = [ "foo" ]; # not an attrset
testAttrs = {
expectedError = {
type = "ThrownError";
msg = "A definition for option `foo' is not of type `attribute set of string'.*";
};
};
};
# listOf string
listOf_str_ok = getMatrix {
outerTypeName = "listOf";
innerTypeName = "str";
value = [
"foo"
"bar"
];
testAttrs = {
expected = [
"foo"
"bar"
];
};
};
listOf_str_err_inner = getMatrix {
outerTypeName = "listOf";
innerTypeName = "str";
value = [
"foo"
1
]; # not a string
testAttrs = {
expectedError = {
type = "ThrownError";
msg = ''A definition for option `foo."\[definition 1-entry 2\]"' is not of type `string'.'';
};
};
};
listOf_str_err_outer = getMatrix {
outerTypeName = "listOf";
innerTypeName = "str";
value = {
foo = 42;
}; # not a list
testAttrs = {
expectedError = {
type = "ThrownError";
msg = "A definition for option `foo' is not of type `list of string'.*";
};
};
};
attrsOf_submodule_ok = getMatrix {
getType =
a: b:
a.attrsOf (
b.submodule (m: {
options.nested = m.lib.mkOption {
type = m.lib.types.str;
};
})
);
outerTypeName = "attrsOf";
innerTypeName = "submodule";
value = {
foo = {
nested = "test1";
};
bar = {
nested = "test2";
};
};
testAttrs = {
expected = {
foo = {
nested = "test1";
};
bar = {
nested = "test2";
};
};
};
};
attrsOf_submodule_err_inner = getMatrix {
outerTypeName = "attrsOf";
innerTypeName = "submodule";
getType =
a: b:
a.attrsOf (
b.submodule (m: {
options.nested = m.lib.mkOption {
type = m.lib.types.str;
};
})
);
value = {
foo = [ 1 ]; # not a submodule
bar = {
nested = "test2";
};
};
testAttrs = {
expectedError = {
type = "ThrownError";
msg = "A definition for option `foo.foo' is not of type `submodule'.*";
};
};
};
attrsOf_submodule_err_outer = getMatrix {
outerTypeName = "attrsOf";
innerTypeName = "submodule";
getType =
a: b:
a.attrsOf (
b.submodule (m: {
options.nested = m.lib.mkOption {
type = m.lib.types.str;
};
})
);
value = [ 123 ]; # not an attrsOf
testAttrs = {
expectedError = {
type = "ThrownError";
msg = ''A definition for option `foo' is not of type `attribute set of \(submodule\).*'';
};
};
};
# either
either_str_attrsOf_ok = getMatrix {
outerTypeName = "either";
innerTypeName = "str_or_attrsOf_str";
getType = a: b: a.either b.str (b.attrsOf a.str);
value = "string value";
testAttrs = {
expected = "string value";
};
};
either_str_attrsOf_err_1 = getMatrix {
outerTypeName = "either";
innerTypeName = "str_or_attrsOf_str";
getType = a: b: a.either b.str (b.attrsOf a.str);
value = 1;
testAttrs = {
expectedError = {
type = "ThrownError";
msg = "A definition for option `foo' is not of type `string or attribute set of string'.*";
};
};
};
either_str_attrsOf_err_2 = getMatrix {
outerTypeName = "either";
innerTypeName = "str_or_attrsOf_str";
getType = a: b: a.either b.str (b.attrsOf a.str);
value = {
bar = 1; # not a string
};
testAttrs = {
expectedError = {
type = "ThrownError";
msg = "A definition for option `foo.bar' is not of type `string'.*";
};
};
};
# Coereced to
coerce_attrsOf_str_to_listOf_str_run = getMatrix {
outerTypeName = "coercedTo";
innerTypeName = "attrsOf_str->listOf_str";
getType = a: b: a.coercedTo (b.attrsOf b.str) builtins.attrValues (b.listOf b.str);
value = {
bar = "test1"; # coerced to listOf string
foo = "test2"; # coerced to listOf string
};
testAttrs = {
expected = [
"test1"
"test2"
];
};
};
coerce_attrsOf_str_to_listOf_str_final = getMatrix {
outerTypeName = "coercedTo";
innerTypeName = "attrsOf_str->listOf_str";
getType = a: b: a.coercedTo (b.attrsOf b.str) (abort "This shouldnt run") (b.listOf b.str);
value = [
"test1"
"test2"
]; # already a listOf string
testAttrs = {
expected = [
"test1"
"test2"
]; # Order should be kept
};
};
coerce_attrsOf_str_to_listOf_err_coercer_input = getMatrix {
outerTypeName = "coercedTo";
innerTypeName = "attrsOf_str->listOf_str";
getType = a: b: a.coercedTo (b.attrsOf b.str) builtins.attrValues (b.listOf b.str);
value = [
{ }
{ }
]; # not coercible to listOf string, with the given coercer
testAttrs = {
expectedError = {
type = "ThrownError";
msg = ''A definition for option `foo."\[definition 1-entry 1\]"' is not of type `string'.*'';
};
};
};
coerce_attrsOf_str_to_listOf_err_coercer_ouput = getMatrix {
outerTypeName = "coercedTo";
innerTypeName = "attrsOf_str->listOf_str";
getType = a: b: a.coercedTo (b.attrsOf b.str) builtins.attrValues (b.listOf b.str);
value = {
foo = {
bar = 1;
}; # coercer produces wrong type -> [ { bar = 1; } ]
};
testAttrs = {
expectedError = {
type = "ThrownError";
msg = ''A definition for option `foo."\[definition 1-entry 1\]"' is not of type `string'.*'';
};
};
};
coerce_str_to_int_coercer_ouput = getMatrix {
outerTypeName = "coercedTo";
innerTypeName = "int->str";
getType = a: b: a.coercedTo b.int toString a.str;
value = [ ];
testAttrs = {
expectedError = {
type = "ThrownError";
msg = ''A definition for option `foo' is not of type `string or signed integer convertible to it.*'';
};
};
};
# Submodule
submodule_with_ok = getMatrix {
outerTypeName = "submoduleWith";
innerTypeName = "mixed_types";
getType =
a: b:
a.submodule (m: {
options.attrs = m.lib.mkOption {
type = b.attrsOf b.str;
};
options.list = m.lib.mkOption {
type = b.listOf b.str;
};
options.either = m.lib.mkOption {
type = b.either a.str a.int;
};
});
value = {
attrs = {
foo = "bar";
};
list = [
"foo"
"bar"
];
either = 123; # int
};
testAttrs = {
expected = {
attrs = {
foo = "bar";
};
list = [
"foo"
"bar"
];
either = 123;
};
};
};
}

165
lib/tests/fetchers.nix Normal file
View File

@@ -0,0 +1,165 @@
let
lib = import ./..;
inherit (lib)
fakeHash
fakeSha256
fakeSha512
flip
functionArgs
runTests
;
inherit (lib.fetchers) normalizeHash withNormalizedHash;
testingThrow = expr: {
expr = with builtins; tryEval (seq expr "didn't throw");
expected = {
success = false;
value = false;
};
};
# hashes of empty
sri256 = "sha256-d6xi4mKdjkX2JFicDIv5niSzpyI0m/Hnm8GGAIU04kY=";
sri512 = "sha512-AXFyVo7jiZ5we10fxZ5E9qfPjSfqkizY2apCzORKFVYZaNhCIVbooY+J4cYST00ztLf0EjivIBPPdtIYFUMfzQ==";
unionOfDisjoints = lib.foldl lib.attrsets.unionOfDisjoint { };
genTests = n: f: {
"test${n}AlreadyNormalized" = {
expr = f { } {
outputHash = "";
outputHashAlgo = "md42";
};
expected = {
outputHash = "";
outputHashAlgo = "md42";
};
};
"test${n}EmptySha256" = {
expr = f { } { sha256 = ""; };
expected = {
outputHash = fakeSha256;
outputHashAlgo = "sha256";
};
};
"test${n}EmptySha512" = {
expr = f { hashTypes = [ "sha512" ]; } { sha512 = ""; };
expected = {
outputHash = fakeSha512;
outputHashAlgo = "sha512";
};
};
"test${n}EmptyHash" = {
expr = f { } { hash = ""; };
expected = {
outputHash = fakeHash;
outputHashAlgo = null;
};
};
"test${n}Sri256" = {
expr = f { } { hash = sri256; };
expected = {
outputHash = sri256;
outputHashAlgo = null;
};
};
"test${n}Sri512" = {
expr = f { } { hash = sri512; };
expected = {
outputHash = sri512;
outputHashAlgo = null;
};
};
"test${n}PreservesAttrs" = {
expr = f { } {
hash = "aaaa";
destination = "Earth";
};
expected = {
outputHash = "aaaa";
outputHashAlgo = null;
destination = "Earth";
};
};
"test${n}RejectsSha1ByDefault" = testingThrow (f { } { sha1 = ""; });
"test${n}RejectsSha512ByDefault" = testingThrow (f { } { sha512 = ""; });
"test${n}ThrowsOnMissing" = testingThrow (f { } { gibi = false; });
};
in
runTests (unionOfDisjoints [
(genTests "NormalizeHash" normalizeHash)
(genTests "WithNormalized" (
flip withNormalizedHash ({ outputHash, outputHashAlgo, ... }@args: args)
))
{
testNormalizeNotRequiredEquivalent = {
expr = normalizeHash { required = false; } {
hash = "";
prof = "shadoko";
};
expected = normalizeHash { } {
hash = "";
prof = "shadoko";
};
};
testNormalizeNotRequiredPassthru = {
expr = normalizeHash { required = false; } { "ga bu" = "zo meu"; };
expected."ga bu" = "zo meu";
};
testOptionalArg = {
expr = withNormalizedHash { } (
{
outputHash ? "",
outputHashAlgo ? null,
...
}@args:
args
) { author = "Jacques Rouxel"; };
expected.author = "Jacques Rouxel";
};
testOptionalArgMetadata = {
expr = functionArgs (
withNormalizedHash { } (
{
outputHash ? "",
outputHashAlgo ? null,
}:
{ }
)
);
expected.hash = true;
};
testPreservesArgsMetadata = {
expr = functionArgs (
withNormalizedHash { } (
{
outputHash,
outputHashAlgo,
pumping ? true,
}:
{ }
)
);
expected = {
hash = false;
pumping = true;
};
};
testRejectsMissingHashArg = testingThrow (withNormalizedHash { } ({ outputHashAlgo }: { }));
testRejectsMissingAlgoArg = testingThrow (withNormalizedHash { } ({ outputHash }: { }));
}
])

90
lib/tests/filesystem.sh Executable file
View File

@@ -0,0 +1,90 @@
#!/usr/bin/env bash
# Tests lib/filesystem.nix
# Run:
# [nixpkgs]$ lib/tests/filesystem.sh
# or:
# [nixpkgs]$ nix-build lib/tests/release.nix
set -euo pipefail
shopt -s inherit_errexit
# Use
# || die
die() {
echo >&2 "test case failed: " "$@"
exit 1
}
if test -n "${TEST_LIB:-}"; then
NIX_PATH=nixpkgs="$(dirname "$TEST_LIB")"
else
NIX_PATH=nixpkgs="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.."; pwd)"
fi
export NIX_PATH
work="$(mktemp -d)"
clean_up() {
rm -rf "$work"
}
trap clean_up EXIT
cd "$work"
mkdir directory
touch regular
ln -s target symlink
mkfifo fifo
expectSuccess() {
local expr=$1
local expectedResultRegex=$2
if ! result=$(nix-instantiate --eval --strict --json \
--expr "with (import <nixpkgs/lib>).filesystem; $expr"); then
die "$expr failed to evaluate, but it was expected to succeed"
fi
if [[ ! "$result" =~ $expectedResultRegex ]]; then
die "$expr == $result, but $expectedResultRegex was expected"
fi
}
expectFailure() {
local expr=$1
local expectedErrorRegex=$2
if result=$(nix-instantiate --eval --strict --json 2>"$work/stderr" \
--expr "with (import <nixpkgs/lib>).filesystem; $expr"); then
die "$expr evaluated successfully to $result, but it was expected to fail"
fi
if [[ ! "$(<"$work/stderr")" =~ $expectedErrorRegex ]]; then
die "Error was $(<"$work/stderr"), but $expectedErrorRegex was expected"
fi
}
expectSuccess "pathType /." '"directory"'
expectSuccess "pathType $PWD/directory" '"directory"'
expectSuccess "pathType $PWD/regular" '"regular"'
expectSuccess "pathType $PWD/symlink" '"symlink"'
expectSuccess "pathType $PWD/fifo" '"unknown"'
# Only check error message when a Nixpkgs-specified error is thrown,
# which is only the case when `readFileType` is not available
# and the fallback implementation needs to be used.
if [[ "$(nix-instantiate --eval --expr 'builtins ? readFileType')" == false ]]; then
expectFailure "pathType $PWD/non-existent" \
"error: evaluation aborted with the following error message: 'lib.filesystem.pathType: Path $PWD/non-existent does not exist.'"
fi
expectSuccess "pathIsDirectory /." "true"
expectSuccess "pathIsDirectory $PWD/directory" "true"
expectSuccess "pathIsDirectory $PWD/regular" "false"
expectSuccess "pathIsDirectory $PWD/symlink" "false"
expectSuccess "pathIsDirectory $PWD/fifo" "false"
expectSuccess "pathIsDirectory $PWD/non-existent" "false"
expectSuccess "pathIsRegularFile /." "false"
expectSuccess "pathIsRegularFile $PWD/directory" "false"
expectSuccess "pathIsRegularFile $PWD/regular" "true"
expectSuccess "pathIsRegularFile $PWD/symlink" "false"
expectSuccess "pathIsRegularFile $PWD/fifo" "false"
expectSuccess "pathIsRegularFile $PWD/non-existent" "false"
echo >&2 tests ok

View File

@@ -0,0 +1,33 @@
{ lib, ... }:
let
inherit (lib) types;
in
{
options = {
name = lib.mkOption {
type = types.str;
};
github = lib.mkOption {
type = types.str;
};
githubId = lib.mkOption {
type = types.ints.unsigned;
};
email = lib.mkOption {
type = types.nullOr types.str;
default = null;
};
matrix = lib.mkOption {
type = types.nullOr types.str;
default = null;
};
keys = lib.mkOption {
type = types.listOf (
types.submodule {
options.fingerprint = lib.mkOption { type = types.str; };
}
);
default = [ ];
};
};
}

76
lib/tests/maintainers.nix Normal file
View File

@@ -0,0 +1,76 @@
# to run these tests (and the others)
# nix-build nixpkgs/lib/tests/release.nix
# These tests should stay in sync with the comment in maintainers/maintainer-list.nix
{
# The pkgs used for dependencies for the testing itself
pkgs ? import ../.. { },
lib ? pkgs.lib,
}:
let
checkMaintainer =
handle: uncheckedAttrs:
let
prefix = [
"lib"
"maintainers"
handle
];
checkedAttrs =
(lib.modules.evalModules {
inherit prefix;
modules = [
./maintainer-module.nix
{
_file = toString ../../maintainers/maintainer-list.nix;
config = uncheckedAttrs;
}
];
}).config;
checks =
lib.optional (checkedAttrs.github != null && checkedAttrs.githubId == null) ''
echo ${lib.escapeShellArg (lib.showOption prefix)}': If `github` is specified, `githubId` must be too.'
# Calling this too often would hit non-authenticated API limits, but this
# shouldn't happen since such errors will get fixed rather quickly
info=$(curl -sS https://api.github.com/users/${checkedAttrs.github})
id=$(jq -r '.id' <<< "$info")
echo "The GitHub ID for GitHub user ${checkedAttrs.github} is $id:"
echo -e " githubId = $id;\n"
''
++
lib.optional
(checkedAttrs.email == null && checkedAttrs.github == null && checkedAttrs.matrix == null)
''
echo ${lib.escapeShellArg (lib.showOption prefix)}': At least one of `email`, `github` or `matrix` must be specified, so that users know how to reach you.'
''
++
lib.optional (checkedAttrs.email != null && lib.hasSuffix "noreply.github.com" checkedAttrs.email)
''
echo ${lib.escapeShellArg (lib.showOption prefix)}': If an email address is given, it should allow people to reach you. If you do not want that, you can just provide `github` or `matrix` instead.'
'';
in
lib.deepSeq checkedAttrs checks;
missingGithubIds = lib.concatLists (lib.mapAttrsToList checkMaintainer lib.maintainers);
success = pkgs.runCommand "checked-maintainers-success" { } "mkdir $out";
failure =
pkgs.runCommand "checked-maintainers-failure"
{
nativeBuildInputs = [
pkgs.curl
pkgs.jq
];
outputHash = "sha256:${lib.fakeSha256}";
outputHAlgo = "sha256";
outputHashMode = "flat";
SSL_CERT_FILE = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt";
}
''
${lib.concatStringsSep "\n" missingGithubIds}
exit 1
'';
in
if missingGithubIds == [ ] then success else failure

4664
lib/tests/misc.nix Normal file

File diff suppressed because it is too large Load Diff

819
lib/tests/modules.sh Executable file
View File

@@ -0,0 +1,819 @@
#!/usr/bin/env bash
# This script is used to test that the module system is working as expected.
# Executing it runs tests for `lib.modules`, `lib.options` and `lib.types`.
# By default it test the version of nixpkgs which is defined in the NIX_PATH.
#
# Run:
# [nixpkgs]$ lib/tests/modules.sh
# or:
# [nixpkgs]$ nix-build lib/tests/release.nix
set -o errexit -o noclobber -o nounset -o pipefail
shopt -s failglob inherit_errexit
# https://stackoverflow.com/a/246128/6605742
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
cd "$DIR"/modules
pass=0
fail=0
local-nix-instantiate() {
nix-instantiate --timeout 1 --eval-only --show-trace --read-write-mode --json "$@"
}
# loc
# prints the location of the call of to the function that calls it
# loc n
# prints the location n levels up the call stack
loc() {
local caller depth
depth=1
if [[ $# -gt 0 ]]; then
depth=$1
fi
# ( lineno fnname file ) of the caller
caller=( $(caller $depth) )
echo "${caller[2]}:${caller[0]}"
}
line() {
echo "----------------------------------------"
}
logStartFailure() {
line
}
logEndFailure() {
line
echo
}
logFailure() {
# bold red
printf '\033[1;31mTEST FAILED\033[0m at %s\n' "$(loc 2)"
}
evalConfig() {
local attr=$1
shift
local script="import ./default.nix { modules = [ $* ];}"
local-nix-instantiate -E "$script" -A "$attr"
}
reportFailure() {
local attr=$1
shift
local script="import ./default.nix { modules = [ $* ];}"
echo "$ nix-instantiate -E '$script' -A '$attr' --eval-only --json"
evalConfig "$attr" "$@" || true
((++fail))
}
checkConfigOutput() {
local outputContains=$1
shift
if evalConfig "$@" 2>/dev/null | grep -E --silent "$outputContains" ; then
((++pass))
else
logStartFailure
echo "ACTUAL:"
reportFailure "$@"
echo "EXPECTED: result matching '$outputContains'"
logFailure
logEndFailure
fi
}
invertIfUnset() {
gate="$1"
shift
if [[ -n "${!gate:-}" ]]; then
"$@"
else
! "$@"
fi
}
globalErrorLogCheck() {
invertIfUnset "REQUIRE_INFINITE_RECURSION_HINT" \
grep -i 'if you get an infinite recursion here' \
<<<"$err" >/dev/null \
|| {
if [[ -n "${REQUIRE_INFINITE_RECURSION_HINT:-}" ]]; then
echo "Unexpected infinite recursion hint"
else
echo "Expected infinite recursion hint, but none found"
fi
return 1
}
}
checkExpression() {
local path=$1
local output
{
output="$(local-nix-instantiate --strict "$path" 2>&1)" && ((++pass))
} || {
logStartFailure
echo "$output"
((++fail))
logFailure
logEndFailure
}
}
checkConfigError() {
local errorContains=$1
local err=""
shift
if err="$(evalConfig "$@" 2>&1 >/dev/null)"; then
logStartFailure
echo "ACTUAL: exit code 0, output:"
reportFailure "$@"
echo "EXPECTED: non-zero exit code"
logFailure
logEndFailure
else
if ! globalErrorLogCheck "$err"; then
logStartFailure
echo "LOG:"
reportFailure "$@"
echo "GLOBAL ERROR LOG CHECK FAILED"
logFailure
logEndFailure
fi
if echo "$err" | grep -zP --silent "$errorContains" ; then
((++pass))
else
logStartFailure
echo "ACTUAL:"
reportFailure "$@"
echo "EXPECTED: error matching '$errorContains'"
logFailure
logEndFailure
fi
fi
}
# Shorthand meta attribute does not duplicate the config
checkConfigOutput '^"one two"$' config.result ./shorthand-meta.nix
checkConfigOutput '^true$' config.result ./test-mergeAttrDefinitionsWithPrio.nix
# Check that a module argument is passed, also when a default is available
# (but not needed)
#
# When the default is needed, we currently fail to do what the users expect, as
# we pass our own argument anyway, even if it *turns out* not to exist.
#
# The reason for this is that we don't know at invocation time what is in the
# _module.args option. That value is only available *after* all modules have been
# invoked.
#
# Hypothetically, Nix could help support this by giving access to the default
# values, through a new built-in function.
# However the default values are allowed to depend on other arguments, so those
# would have to be passed in somehow, making this not just a getter but
# something more complicated.
#
# At that point we have to wonder whether the extra complexity is worth the cost.
# Another - subjective - reason not to support it is that default values
# contradict the notion that an option has a single value, where _module.args
# is the option.
checkConfigOutput '^true$' config.result ./module-argument-default.nix
# gvariant
checkConfigOutput '^true$' config.assertion ./gvariant.nix
checkConfigOutput '"ok"' config.result ./specialArgs-lib.nix
# https://github.com/NixOS/nixpkgs/pull/131205
# We currently throw this error already in `config`, but throwing in `config.wrong1` would be acceptable.
checkConfigError 'It seems as if you.re trying to declare an option by placing it into .config. rather than .options.' config.wrong1 ./error-mkOption-in-config.nix
# We currently throw this error already in `config`, but throwing in `config.nest.wrong2` would be acceptable.
checkConfigError 'It seems as if you.re trying to declare an option by placing it into .config. rather than .options.' config.nest.wrong2 ./error-mkOption-in-config.nix
checkConfigError 'The option .sub.wrong2. does not exist. Definition values:' config.sub ./error-mkOption-in-submodule-config.nix
checkConfigError '.*This can happen if you e.g. declared your options in .types.submodule.' config.sub ./error-mkOption-in-submodule-config.nix
checkConfigError '.*A definition for option .bad. is not of type .non-empty .list of .submodule...\.' config.bad ./error-nonEmptyListOf-submodule.nix
# types.attrTag
checkConfigOutput '^true$' config.okChecks ./types-attrTag.nix
checkConfigError 'A definition for option .intStrings\.syntaxError. is not of type .attribute-tagged union' config.intStrings.syntaxError ./types-attrTag.nix
checkConfigError 'A definition for option .intStrings\.syntaxError2. is not of type .attribute-tagged union' config.intStrings.syntaxError2 ./types-attrTag.nix
checkConfigError 'A definition for option .intStrings\.syntaxError3. is not of type .attribute-tagged union' config.intStrings.syntaxError3 ./types-attrTag.nix
checkConfigError 'A definition for option .intStrings\.syntaxError4. is not of type .attribute-tagged union' config.intStrings.syntaxError4 ./types-attrTag.nix
checkConfigError 'A definition for option .intStrings\.mergeError. is not of type .attribute-tagged union' config.intStrings.mergeError ./types-attrTag.nix
checkConfigError 'A definition for option .intStrings\.badTagError. is not of type .attribute-tagged union' config.intStrings.badTagError ./types-attrTag.nix
checkConfigError 'A definition for option .intStrings\.badTagTypeError\.left. is not of type .signed integer.' config.intStrings.badTagTypeError.left ./types-attrTag.nix
checkConfigError 'A definition for option .nested\.right\.left. is not of type .signed integer.' config.nested.right.left ./types-attrTag.nix
checkConfigError 'In attrTag, each tag value must be an option, but tag int was a bare type, not wrapped in mkOption.' config.opt.int ./types-attrTag-wrong-decl.nix
# types
checkConfigOutput '"ok"' config.assertions ./types.nix
# types.pathInStore
checkConfigOutput '".*/store/0lz9p8xhf89kb1c1kk6jxrzskaiygnlh-bash-5.2-p15.drv"' config.pathInStore.ok1 ./types.nix
checkConfigOutput '".*/store/0fb3ykw9r5hpayd05sr0cizwadzq1d8q-bash-5.2-p15"' config.pathInStore.ok2 ./types.nix
checkConfigOutput '".*/store/0fb3ykw9r5hpayd05sr0cizwadzq1d8q-bash-5.2-p15/bin/bash"' config.pathInStore.ok3 ./types.nix
checkConfigError 'A definition for option .* is not of type .path in the Nix store.. Definition values:\n\s*- In .*: ""' config.pathInStore.bad1 ./types.nix
checkConfigError 'A definition for option .* is not of type .path in the Nix store.. Definition values:\n\s*- In .*: ".*/store"' config.pathInStore.bad2 ./types.nix
checkConfigError 'A definition for option .* is not of type .path in the Nix store.. Definition values:\n\s*- In .*: ".*/store/"' config.pathInStore.bad3 ./types.nix
checkConfigError 'A definition for option .* is not of type .path in the Nix store.. Definition values:\n\s*- In .*: ".*/store/.links"' config.pathInStore.bad4 ./types.nix
checkConfigError 'A definition for option .* is not of type .path in the Nix store.. Definition values:\n\s*- In .*: "/foo/bar"' config.pathInStore.bad5 ./types.nix
# Check boolean option.
checkConfigOutput '^false$' config.enable ./declare-enable.nix
checkConfigError 'The option .* does not exist. Definition values:\n\s*- In .*: true' config.enable ./define-enable.nix
checkConfigError 'The option .* does not exist. Definition values:\n\s*- In .*' config.enable ./define-enable-throw.nix
checkConfigError 'while evaluating a definition from `.*/define-enable-abort.nix' config.enable ./define-enable-abort.nix
checkConfigError 'while evaluating the error message for definitions for .enable., which is an option that does not exist' config.enable ./define-enable-abort.nix
# Check boolByOr type.
checkConfigOutput '^false$' config.value.falseFalse ./boolByOr.nix
checkConfigOutput '^true$' config.value.trueFalse ./boolByOr.nix
checkConfigOutput '^true$' config.value.falseTrue ./boolByOr.nix
checkConfigOutput '^true$' config.value.trueTrue ./boolByOr.nix
checkConfigOutput '^1$' config.bare-submodule.nested ./declare-bare-submodule.nix ./declare-bare-submodule-nested-option.nix
checkConfigOutput '^2$' config.bare-submodule.deep ./declare-bare-submodule.nix ./declare-bare-submodule-deep-option.nix
checkConfigOutput '^42$' config.bare-submodule.nested ./declare-bare-submodule.nix ./declare-bare-submodule-nested-option.nix ./declare-bare-submodule-deep-option.nix ./define-bare-submodule-values.nix
checkConfigOutput '^420$' config.bare-submodule.deep ./declare-bare-submodule.nix ./declare-bare-submodule-nested-option.nix ./declare-bare-submodule-deep-option.nix ./define-bare-submodule-values.nix
checkConfigOutput '^2$' config.bare-submodule.deep ./declare-bare-submodule.nix ./declare-bare-submodule-deep-option.nix ./define-shorthandOnlyDefinesConfig-true.nix
checkConfigError 'The option .bare-submodule.deep. in .*/declare-bare-submodule-deep-option.nix. is already declared in .*/declare-bare-submodule-deep-option-duplicate.nix' config.bare-submodule.deep ./declare-bare-submodule.nix ./declare-bare-submodule-deep-option.nix ./declare-bare-submodule-deep-option-duplicate.nix
# Check that strMatching can be merged
checkConfigOutput '^"strMatching.*"$' options.sm.type.name ./strMatching-merge.nix
# Check integer types.
# unsigned
checkConfigOutput '^42$' config.value ./declare-int-unsigned-value.nix ./define-value-int-positive.nix
checkConfigError 'A definition for option .* is not of type.*unsigned integer.*. Definition values:\n\s*- In .*: -23' config.value ./declare-int-unsigned-value.nix ./define-value-int-negative.nix
# positive
checkConfigError 'A definition for option .* is not of type.*positive integer.*. Definition values:\n\s*- In .*: 0' config.value ./declare-int-positive-value.nix ./define-value-int-zero.nix
# between
checkConfigOutput '^42$' config.value ./declare-int-between-value.nix ./define-value-int-positive.nix
checkConfigError 'A definition for option .* is not of type.*between.*-21 and 43.*inclusive.*. Definition values:\n\s*- In .*: -23' config.value ./declare-int-between-value.nix ./define-value-int-negative.nix
# Check either types
# types.either
checkConfigOutput '^42$' config.value ./declare-either.nix ./define-value-int-positive.nix
checkConfigOutput '^"24"$' config.value ./declare-either.nix ./define-value-string.nix
# types.oneOf
checkConfigOutput '^42$' config.value ./declare-oneOf.nix ./define-value-int-positive.nix
checkConfigOutput '^\[\]$' config.value ./declare-oneOf.nix ./define-value-list.nix
checkConfigOutput '^"24"$' config.value ./declare-oneOf.nix ./define-value-string.nix
# Check mkForce without submodules.
set -- config.enable ./declare-enable.nix ./define-enable.nix
checkConfigOutput '^true$' "$@"
checkConfigOutput '^false$' "$@" ./define-force-enable.nix
checkConfigOutput '^false$' "$@" ./define-enable-force.nix
# Check mkForce with option and submodules.
checkConfigError 'attribute .*foo.* .* not found' config.attrsOfSub.foo.enable ./declare-attrsOfSub-any-enable.nix
checkConfigOutput '^false$' config.attrsOfSub.foo.enable ./declare-attrsOfSub-any-enable.nix ./define-attrsOfSub-foo.nix
set -- config.attrsOfSub.foo.enable ./declare-attrsOfSub-any-enable.nix ./define-attrsOfSub-foo-enable.nix
checkConfigOutput '^true$' "$@"
checkConfigOutput '^false$' "$@" ./define-force-attrsOfSub-foo-enable.nix
checkConfigOutput '^false$' "$@" ./define-attrsOfSub-force-foo-enable.nix
checkConfigOutput '^false$' "$@" ./define-attrsOfSub-foo-force-enable.nix
checkConfigOutput '^false$' "$@" ./define-attrsOfSub-foo-enable-force.nix
# Check overriding effect of mkForce on submodule definitions.
checkConfigError 'attribute .*bar.* .* not found' config.attrsOfSub.bar.enable ./declare-attrsOfSub-any-enable.nix ./define-attrsOfSub-foo.nix
checkConfigOutput '^false$' config.attrsOfSub.bar.enable ./declare-attrsOfSub-any-enable.nix ./define-attrsOfSub-foo.nix ./define-attrsOfSub-bar.nix
set -- config.attrsOfSub.bar.enable ./declare-attrsOfSub-any-enable.nix ./define-attrsOfSub-foo.nix ./define-attrsOfSub-bar-enable.nix
checkConfigOutput '^true$' "$@"
checkConfigError 'attribute .*bar.* .* not found' "$@" ./define-force-attrsOfSub-foo-enable.nix
checkConfigError 'attribute .*bar.* .* not found' "$@" ./define-attrsOfSub-force-foo-enable.nix
checkConfigOutput '^true$' "$@" ./define-attrsOfSub-foo-force-enable.nix
checkConfigOutput '^true$' "$@" ./define-attrsOfSub-foo-enable-force.nix
# Check mkIf with submodules.
checkConfigError 'attribute .*foo.* .* not found' config.attrsOfSub.foo.enable ./declare-enable.nix ./declare-attrsOfSub-any-enable.nix
set -- config.attrsOfSub.foo.enable ./declare-enable.nix ./declare-attrsOfSub-any-enable.nix
checkConfigError 'attribute .*foo.* .* not found' "$@" ./define-if-attrsOfSub-foo-enable.nix
checkConfigError 'attribute .*foo.* .* not found' "$@" ./define-attrsOfSub-if-foo-enable.nix
checkConfigError 'attribute .*foo.* .* not found' "$@" ./define-attrsOfSub-foo-if-enable.nix
checkConfigOutput '^false$' "$@" ./define-attrsOfSub-foo-enable-if.nix
checkConfigOutput '^true$' "$@" ./define-enable.nix ./define-if-attrsOfSub-foo-enable.nix
checkConfigOutput '^true$' "$@" ./define-enable.nix ./define-attrsOfSub-if-foo-enable.nix
checkConfigOutput '^true$' "$@" ./define-enable.nix ./define-attrsOfSub-foo-if-enable.nix
checkConfigOutput '^true$' "$@" ./define-enable.nix ./define-attrsOfSub-foo-enable-if.nix
# Check importApply
checkConfigOutput '"abc"' config.value ./importApply.nix
# importApply does not set a key.
# Disabling the function file is not sufficient, because importApply can't reasonably assume that the key is unique.
# e.g. user may call it multiple times with different arguments and expect each of the module to apply.
# While this is excusable for the disabledModules aspect, it is not for the deduplication of modules.
checkConfigOutput '"abc"' config.value ./importApply-disabling.nix
# Check disabledModules with config definitions and option declarations.
set -- config.enable ./define-enable.nix ./declare-enable.nix
checkConfigOutput '^true$' "$@"
checkConfigOutput '^false$' "$@" ./disable-define-enable.nix
checkConfigOutput '^false$' "$@" ./disable-define-enable-string-path.nix
checkConfigError "The option .*enable.* does not exist. Definition values:\n\s*- In .*: true" "$@" ./disable-declare-enable.nix
checkConfigError "attribute .*enable.* in selection path .*config.enable.* not found" "$@" ./disable-define-enable.nix ./disable-declare-enable.nix
checkConfigError "attribute .*enable.* in selection path .*config.enable.* not found" "$@" ./disable-enable-modules.nix
checkConfigOutput '^true$' 'config.positive.enable' ./disable-module-with-key.nix
checkConfigOutput '^false$' 'config.negative.enable' ./disable-module-with-key.nix
checkConfigError 'Module ..*disable-module-bad-key.nix. contains a disabledModules item that is an attribute set, presumably a module, that does not have a .key. attribute. .*' 'config.enable' ./disable-module-bad-key.nix
# Not sure if we want to keep supporting module keys that aren't strings, paths or v?key, but we shouldn't remove support accidentally.
checkConfigOutput '^true$' 'config.positive.enable' ./disable-module-with-toString-key.nix
checkConfigOutput '^false$' 'config.negative.enable' ./disable-module-with-toString-key.nix
# Check _module.args.
set -- config.enable ./declare-enable.nix ./define-enable-with-custom-arg.nix
checkConfigError 'while evaluating the module argument .*custom.* in .*define-enable-with-custom-arg.nix.*:' "$@"
checkConfigOutput '^true$' "$@" ./define-_module-args-custom.nix
# Check that using _module.args on imports cause infinite recursions, with
# the proper error context.
set -- "$@" ./define-_module-args-custom.nix ./import-custom-arg.nix
REQUIRE_INFINITE_RECURSION_HINT=1 checkConfigError 'while evaluating the module argument .*custom.* in .*import-custom-arg.nix.*:' "$@"
REQUIRE_INFINITE_RECURSION_HINT=1 checkConfigError 'infinite recursion encountered' "$@"
# Check _module.check.
set -- config.enable ./declare-enable.nix ./define-enable.nix ./define-attrsOfSub-foo.nix
checkConfigError 'The option .* does not exist. Definition values:\n\s*- In .*' "$@"
checkConfigOutput '^true$' "$@" ./define-module-check.nix
# Check coerced value.
set --
checkConfigOutput '^"42"$' config.value ./declare-coerced-value.nix
checkConfigOutput '^"24"$' config.value ./declare-coerced-value.nix ./define-value-string.nix
checkConfigError 'A definition for option .*. is not of type .*.\n\s*- In .*: \[ \]' config.value ./declare-coerced-value.nix ./define-value-list.nix
# Check coerced option merging.
checkConfigError 'The option .value. in .*/declare-coerced-value.nix. is already declared in .*/declare-coerced-value-no-default.nix.' config.value ./declare-coerced-value.nix ./declare-coerced-value-no-default.nix
# Check coerced value with unsound coercion
checkConfigOutput '^12$' config.value ./declare-coerced-value-unsound.nix
checkConfigError 'A definition for option .* is not of type .*.\n\s*- In .*: "1000"' config.value ./declare-coerced-value-unsound.nix ./define-value-string-bigint.nix
checkConfigError 'toInt: Could not convert .* to int' config.value ./declare-coerced-value-unsound.nix ./define-value-string-arbitrary.nix
# Check `graph` attribute
checkExpression './graph/test.nix'
# Check mkAliasOptionModule.
checkConfigOutput '^true$' config.enable ./alias-with-priority.nix
checkConfigOutput '^true$' config.enableAlias ./alias-with-priority.nix
checkConfigOutput '^false$' config.enable ./alias-with-priority-can-override.nix
checkConfigOutput '^false$' config.enableAlias ./alias-with-priority-can-override.nix
# Check mkPackageOption
checkConfigOutput '^"hello"$' config.package.pname ./declare-mkPackageOption.nix
checkConfigOutput '^"hello"$' config.namedPackage.pname ./declare-mkPackageOption.nix
checkConfigOutput '^".*Hello.*"$' options.namedPackage.description ./declare-mkPackageOption.nix
checkConfigOutput '^"literalExpression"$' options.namedPackage.defaultText._type ./declare-mkPackageOption.nix
checkConfigOutput '^"pkgs\.hello"$' options.namedPackage.defaultText.text ./declare-mkPackageOption.nix
checkConfigOutput '^"hello"$' config.namedPackageSingletonDefault.pname ./declare-mkPackageOption.nix
checkConfigOutput '^".*Hello.*"$' options.namedPackageSingletonDefault.description ./declare-mkPackageOption.nix
checkConfigOutput '^"pkgs\.hello"$' options.namedPackageSingletonDefault.defaultText.text ./declare-mkPackageOption.nix
checkConfigOutput '^"hello"$' config.pathPackage.pname ./declare-mkPackageOption.nix
checkConfigOutput '^"literalExpression"$' options.packageWithExample.example._type ./declare-mkPackageOption.nix
checkConfigOutput '^"pkgs\.hello\.override \{ stdenv = pkgs\.clangStdenv; \}"$' options.packageWithExample.example.text ./declare-mkPackageOption.nix
checkConfigOutput '^"literalExpression"$' options.packageWithPathExample.example._type ./declare-mkPackageOption.nix
checkConfigOutput '^"pkgs\.hello"$' options.packageWithPathExample.example.text ./declare-mkPackageOption.nix
checkConfigOutput '^".*Example extra description\..*"$' options.packageWithExtraDescription.description ./declare-mkPackageOption.nix
checkConfigError 'The option .undefinedPackage. was accessed but has no value defined. Try setting the option.' config.undefinedPackage ./declare-mkPackageOption.nix
checkConfigOutput '^null$' config.nullablePackage ./declare-mkPackageOption.nix
checkConfigOutput '^"null or package"$' options.nullablePackage.type.description ./declare-mkPackageOption.nix
checkConfigOutput '^"hello"$' config.nullablePackageWithDefault.pname ./declare-mkPackageOption.nix
checkConfigOutput '^"myPkgs\.hello"$' options.packageWithPkgsText.defaultText.text ./declare-mkPackageOption.nix
checkConfigOutput '^"hello-other"$' options.packageFromOtherSet.default.pname ./declare-mkPackageOption.nix
checkConfigOutput '^"hello"$' config.packageInvalidIdentifier.pname ./declare-mkPackageOption.nix
checkConfigOutput '^"pkgs\.\\"123\\"\.\\"with\\\\\\"quote\\"\.hello"$' options.packageInvalidIdentifier.defaultText.text ./declare-mkPackageOption.nix
checkConfigOutput '^"pkgs\.\\"123\\"\.\\"with\\\\\\"quote\\"\.hello"$' options.packageInvalidIdentifierExample.example.text ./declare-mkPackageOption.nix
# submoduleWith
## specialArgs should work
checkConfigOutput '^"foo"$' config.submodule.foo ./declare-submoduleWith-special.nix
## shorthandOnlyDefines config behaves as expected
checkConfigOutput '^true$' config.submodule.config ./declare-submoduleWith-shorthand.nix ./define-submoduleWith-shorthand.nix
checkConfigError 'is not of type `boolean' config.submodule.config ./declare-submoduleWith-shorthand.nix ./define-submoduleWith-noshorthand.nix
checkConfigError "In module ..*define-submoduleWith-shorthand.nix., you're trying to define a value of type \`bool'\n\s*rather than an attribute set for the option" config.submodule.config ./declare-submoduleWith-noshorthand.nix ./define-submoduleWith-shorthand.nix
checkConfigOutput '^true$' config.submodule.config ./declare-submoduleWith-noshorthand.nix ./define-submoduleWith-noshorthand.nix
## submoduleWith should merge all modules in one swoop
checkConfigOutput '^true$' config.submodule.inner ./declare-submoduleWith-modules.nix
checkConfigOutput '^true$' config.submodule.outer ./declare-submoduleWith-modules.nix
# Should also be able to evaluate the type name (which evaluates freeformType,
# which evaluates all the modules defined by the type)
checkConfigOutput '^"submodule"$' options.submodule.type.description ./declare-submoduleWith-modules.nix
## submodules can be declared using (evalModules {...}).type
checkConfigOutput '^true$' config.submodule.inner ./declare-submodule-via-evalModules.nix
checkConfigOutput '^true$' config.submodule.outer ./declare-submodule-via-evalModules.nix
# Should also be able to evaluate the type name (which evaluates freeformType,
# which evaluates all the modules defined by the type)
checkConfigOutput '^"submodule"$' options.submodule.type.description ./declare-submodule-via-evalModules.nix
## Paths should be allowed as values and work as expected
checkConfigOutput '^true$' config.submodule.enable ./declare-submoduleWith-path.nix
## _prefix module argument is available at import time and contains the prefix
checkConfigOutput '^true$' config.foo.ok ./prefix-module-argument.nix
## deferredModule
# default module is merged into nodes.foo
checkConfigOutput '"beta"' config.nodes.foo.settingsDict.c ./deferred-module.nix
# errors from the default module are reported with accurate location
checkConfigError 'In `the-file-that-contains-the-bad-config.nix, via option default'\'': "bogus"' config.nodes.foo.bottom ./deferred-module.nix
checkConfigError '.*lib/tests/modules/deferred-module-error.nix, via option deferred [(]:anon-1:anon-1:anon-1[)] does not look like a module.' config.result ./deferred-module-error.nix
# Check the file location information is propagated into submodules
checkConfigOutput the-file.nix config.submodule.internalFiles.0 ./submoduleFiles.nix
# Check that disabledModules works recursively and correctly
checkConfigOutput '^true$' config.enable ./disable-recursive/main.nix
checkConfigOutput '^true$' config.enable ./disable-recursive/{main.nix,disable-foo.nix}
checkConfigOutput '^true$' config.enable ./disable-recursive/{main.nix,disable-bar.nix}
checkConfigError 'The option .* does not exist. Definition values:\n\s*- In .*: true' config.enable ./disable-recursive/{main.nix,disable-foo.nix,disable-bar.nix}
# Check that imports can depend on derivations
checkConfigOutput '^true$' config.enable ./import-from-store.nix
# Check that configs can be conditional on option existence
checkConfigOutput '^true$' config.enable ./define-option-dependently.nix ./declare-enable.nix ./declare-int-positive-value.nix
checkConfigOutput '^360$' config.value ./define-option-dependently.nix ./declare-enable.nix ./declare-int-positive-value.nix
checkConfigOutput '^7$' config.value ./define-option-dependently.nix ./declare-int-positive-value.nix
checkConfigOutput '^true$' config.set.enable ./define-option-dependently-nested.nix ./declare-enable-nested.nix ./declare-int-positive-value-nested.nix
checkConfigOutput '^360$' config.set.value ./define-option-dependently-nested.nix ./declare-enable-nested.nix ./declare-int-positive-value-nested.nix
checkConfigOutput '^7$' config.set.value ./define-option-dependently-nested.nix ./declare-int-positive-value-nested.nix
# Check attrsOf and lazyAttrsOf. Only lazyAttrsOf should be lazy, and only
# attrsOf should work with conditional definitions
# In addition, lazyAttrsOf should honor an options emptyValue
checkConfigError "is not lazy" config.isLazy ./declare-attrsOf.nix ./attrsOf-lazy-check.nix
checkConfigOutput '^true$' config.isLazy ./declare-lazyAttrsOf.nix ./attrsOf-lazy-check.nix
checkConfigOutput '^true$' config.conditionalWorks ./declare-attrsOf.nix ./attrsOf-conditional-check.nix
checkConfigOutput '^false$' config.conditionalWorks ./declare-lazyAttrsOf.nix ./attrsOf-conditional-check.nix
checkConfigOutput '^"empty"$' config.value.foo ./declare-lazyAttrsOf.nix ./attrsOf-conditional-check.nix
# Check attrsWith type merging
checkConfigError 'The option `mergedLazyNonLazy'\'' in `.*'\'' is already declared in `.*'\''\.' options.mergedLazyNonLazy ./lazy-attrsWith.nix
checkConfigOutput '^11$' config.lazyResult ./lazy-attrsWith.nix
checkConfigError 'infinite recursion encountered' config.nonLazyResult ./lazy-attrsWith.nix
# AttrsWith placeholder tests
checkConfigOutput '^"mergedName.<id>.nested"$' config.result ./name-merge-attrsWith-1.nix
checkConfigError 'The option .mergedName. in .*\.nix. is already declared in .*\.nix' config.mergedName ./name-merge-attrsWith-2.nix
# Test type.functor.wrapped deprecation warning
# should emit the warning on:
# - merged types
# - non-merged types
# - nestedTypes elemType
# attrsWith
NIX_ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.attrsWith.type.functor.wrapped ./deprecated-wrapped.nix
NIX_ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.mergedAttrsWith.type.functor.wrapped ./deprecated-wrapped.nix
NIX_ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.attrsWith.type.nestedTypes.elemType.functor.wrapped ./deprecated-wrapped.nix
NIX_ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.mergedAttrsWith.type.nestedTypes.elemType.functor.wrapped ./deprecated-wrapped.nix
# listOf
NIX_ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.listOf.type.functor.wrapped ./deprecated-wrapped.nix
NIX_ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.mergedListOf.type.functor.wrapped ./deprecated-wrapped.nix
NIX_ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.listOf.type.nestedTypes.elemType.functor.wrapped ./deprecated-wrapped.nix
NIX_ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.mergedListOf.type.nestedTypes.elemType.functor.wrapped ./deprecated-wrapped.nix
# unique / uniq
NIX_ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.unique.type.functor.wrapped ./deprecated-wrapped.nix
NIX_ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.mergedUnique.type.functor.wrapped ./deprecated-wrapped.nix
NIX_ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.unique.type.nestedTypes.elemType.functor.wrapped ./deprecated-wrapped.nix
NIX_ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.mergedUnique.type.nestedTypes.elemType.functor.wrapped ./deprecated-wrapped.nix
# nullOr
NIX_ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.nullOr.type.functor.wrapped ./deprecated-wrapped.nix
NIX_ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.mergedNullOr.type.functor.wrapped ./deprecated-wrapped.nix
NIX_ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.nullOr.type.nestedTypes.elemType.functor.wrapped ./deprecated-wrapped.nix
NIX_ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.mergedNullOr.type.nestedTypes.elemType.functor.wrapped ./deprecated-wrapped.nix
# functionTo
NIX_ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.functionTo.type.functor.wrapped ./deprecated-wrapped.nix
NIX_ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.mergedFunctionTo.type.functor.wrapped ./deprecated-wrapped.nix
NIX_ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.functionTo.type.nestedTypes.elemType.functor.wrapped ./deprecated-wrapped.nix
NIX_ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.mergedFunctionTo.type.nestedTypes.elemType.functor.wrapped ./deprecated-wrapped.nix
# coercedTo
# Note: test 'nestedTypes.finalType' and 'nestedTypes.coercedType'
NIX_ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.coercedTo.type.functor.wrapped ./deprecated-wrapped.nix
NIX_ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.coercedTo.type.nestedTypes.finalType.functor.wrapped ./deprecated-wrapped.nix
NIX_ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.coercedTo.type.nestedTypes.coercedType.functor.wrapped ./deprecated-wrapped.nix
# either
NIX_ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.either.type.functor.wrapped ./deprecated-wrapped.nix
NIX_ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.mergedEither.type.functor.wrapped ./deprecated-wrapped.nix
NIX_ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.either.type.nestedTypes.left.functor.wrapped ./deprecated-wrapped.nix
NIX_ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.either.type.nestedTypes.right.functor.wrapped ./deprecated-wrapped.nix
NIX_ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.mergedEither.type.nestedTypes.left.functor.wrapped ./deprecated-wrapped.nix
NIX_ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.mergedEither.type.nestedTypes.right.functor.wrapped ./deprecated-wrapped.nix
# Even with multiple assignments, a type error should be thrown if any of them aren't valid
checkConfigError 'A definition for option .* is not of type .*' \
config.value ./declare-int-unsigned-value.nix ./define-value-list.nix ./define-value-int-positive.nix
## Freeform modules
# Assigning without a declared option should work
checkConfigOutput '^"24"$' config.value ./freeform-attrsOf.nix ./define-value-string.nix
# Shorthand modules interpret `meta` and `class` as config items
checkConfigOutput '^true$' options._module.args.value.result ./freeform-attrsOf.nix ./define-freeform-keywords-shorthand.nix
# No freeform assignments shouldn't make it error
checkConfigOutput '^{}$' config ./freeform-attrsOf.nix
# but only if the type matches
checkConfigError 'A definition for option .* is not of type .*' config.value ./freeform-attrsOf.nix ./define-value-list.nix
# and properties should be applied
checkConfigOutput '^"yes"$' config.value ./freeform-attrsOf.nix ./define-value-string-properties.nix
# Options should still be declarable, and be able to have a type that doesn't match the freeform type
checkConfigOutput '^false$' config.enable ./freeform-attrsOf.nix ./define-value-string.nix ./declare-enable.nix
checkConfigOutput '^"24"$' config.value ./freeform-attrsOf.nix ./define-value-string.nix ./declare-enable.nix
# and this should work too with nested values
checkConfigOutput '^false$' config.nest.foo ./freeform-attrsOf.nix ./freeform-nested.nix
checkConfigOutput '^"bar"$' config.nest.bar ./freeform-attrsOf.nix ./freeform-nested.nix
# Check whether a declared option can depend on an freeform-typed one
checkConfigOutput '^null$' config.foo ./freeform-attrsOf.nix ./freeform-str-dep-unstr.nix
checkConfigOutput '^"24"$' config.foo ./freeform-attrsOf.nix ./freeform-str-dep-unstr.nix ./define-value-string.nix
# Check whether an freeform-typed value can depend on a declared option, this can only work with lazyAttrsOf
REQUIRE_INFINITE_RECURSION_HINT=1 checkConfigError 'infinite recursion encountered' config.foo ./freeform-attrsOf.nix ./freeform-unstr-dep-str.nix
checkConfigError 'The option .* was accessed but has no value defined. Try setting the option.' config.foo ./freeform-lazyAttrsOf.nix ./freeform-unstr-dep-str.nix
checkConfigOutput '^"24"$' config.foo ./freeform-lazyAttrsOf.nix ./freeform-unstr-dep-str.nix ./define-value-string.nix
# submodules in freeformTypes should have their locations annotated
checkConfigOutput '/freeform-submodules.nix"$' config.fooDeclarations.0 ./freeform-submodules.nix
# freeformTypes can get merged using `types.type`, including submodules
checkConfigOutput '^10$' config.free.xxx.foo ./freeform-submodules.nix
checkConfigOutput '^10$' config.free.yyy.bar ./freeform-submodules.nix
# Regression of either, due to freeform not beeing checked previously
checkConfigOutput '^"foo"$' config.either.int ./freeform-deprecated-malicous.nix ./freeform-deprecated-malicous-wrong.nix
NIX_ABORT_ON_WARN=1 checkConfigError "One or more definitions did not pass the type-check of the \'either\' type" config.either.int ./freeform-deprecated-malicous.nix ./freeform-deprecated-malicous-wrong.nix
checkConfigOutput '^"foo"$' config.eitherBehindNullor.int ./freeform-deprecated-malicous.nix ./freeform-deprecated-malicous-wrong.nix
NIX_ABORT_ON_WARN=1 checkConfigError "One or more definitions did not pass the type-check of the \'either\' type" config.eitherBehindNullor.int ./freeform-deprecated-malicous.nix ./freeform-deprecated-malicous-wrong.nix
checkConfigOutput '^"foo"$' config.oneOf.int ./freeform-deprecated-malicous.nix ./freeform-deprecated-malicous-wrong.nix
NIX_ABORT_ON_WARN=1 checkConfigError "One or more definitions did not pass the type-check of the \'either\' type" config.oneOf.int ./freeform-deprecated-malicous.nix ./freeform-deprecated-malicous-wrong.nix
checkConfigOutput '^"foo"$' config.number.str ./freeform-deprecated-malicous.nix ./freeform-deprecated-malicous-wrong.nix
NIX_ABORT_ON_WARN=1 checkConfigError "One or more definitions did not pass the type-check of the \'either\' type" config.number.str ./freeform-deprecated-malicous.nix ./freeform-deprecated-malicous-wrong.nix
checkConfigOutput '^42$' config.either.int ./freeform-deprecated-malicous.nix ./freeform-deprecated-malicous-wrong2.nix
NIX_ABORT_ON_WARN=1 checkConfigError "One or more definitions did not pass the type-check of the \'either\' type" config.either.int ./freeform-deprecated-malicous.nix ./freeform-deprecated-malicous-wrong2.nix
checkConfigOutput '^42$' config.eitherBehindNullor.int ./freeform-deprecated-malicous.nix ./freeform-deprecated-malicous-wrong2.nix
NIX_ABORT_ON_WARN=1 checkConfigError "One or more definitions did not pass the type-check of the \'either\' type" config.eitherBehindNullor.int ./freeform-deprecated-malicous.nix ./freeform-deprecated-malicous-wrong2.nix
checkConfigOutput '^42$' config.oneOf.int ./freeform-deprecated-malicous.nix ./freeform-deprecated-malicous-wrong2.nix
NIX_ABORT_ON_WARN=1 checkConfigError "One or more definitions did not pass the type-check of the \'either\' type" config.oneOf.int ./freeform-deprecated-malicous.nix ./freeform-deprecated-malicous-wrong2.nix
checkConfigOutput '^42$' config.number.str ./freeform-deprecated-malicous.nix ./freeform-deprecated-malicous-wrong2.nix
NIX_ABORT_ON_WARN=1 checkConfigError "One or more definitions did not pass the type-check of the \'either\' type" config.number.str ./freeform-deprecated-malicous.nix ./freeform-deprecated-malicous-wrong2.nix
# Value OK: Fail if a warning is emitted
NIX_ABORT_ON_WARN=1 checkConfigOutput "^42$" config.number.int ./freeform-attrsof-either.nix
## types.anything
# Check that attribute sets are merged recursively
checkConfigOutput '^null$' config.value.foo ./types-anything/nested-attrs.nix
checkConfigOutput '^null$' config.value.l1.foo ./types-anything/nested-attrs.nix
checkConfigOutput '^null$' config.value.l1.l2.foo ./types-anything/nested-attrs.nix
checkConfigOutput '^null$' config.value.l1.l2.l3.foo ./types-anything/nested-attrs.nix
# Attribute sets that are coercible to strings shouldn't be recursed into
checkConfigOutput '^"foo"$' config.value.outPath ./types-anything/attrs-coercible.nix
# Multiple lists aren't concatenated together if their definitions are not equal
checkConfigError 'The option .* has conflicting definition values' config.value ./types-anything/lists.nix
# Check that all equalizable atoms can be used as long as all definitions are equal
checkConfigOutput '^0$' config.value.int ./types-anything/equal-atoms.nix
checkConfigOutput '^false$' config.value.bool ./types-anything/equal-atoms.nix
checkConfigOutput '^""$' config.value.string ./types-anything/equal-atoms.nix
checkConfigOutput '^"/[^"]+"$' config.value.path ./types-anything/equal-atoms.nix
checkConfigOutput '^null$' config.value.null ./types-anything/equal-atoms.nix
checkConfigOutput '^0.1$' config.value.float ./types-anything/equal-atoms.nix
checkConfigOutput '^\[1,"a",{"x":null}\]$' config.value.list ./types-anything/equal-atoms.nix
# Functions can't be merged together
checkConfigError "The option .value.multiple-lambdas.<function body>. has conflicting option types" config.applied.multiple-lambdas ./types-anything/functions.nix
checkConfigOutput '^true$' config.valueIsFunction.single-lambda ./types-anything/functions.nix
checkConfigOutput '^null$' config.applied.merging-lambdas.x ./types-anything/functions.nix
checkConfigOutput '^null$' config.applied.merging-lambdas.y ./types-anything/functions.nix
# Check that all mk* modifiers are applied
checkConfigError 'attribute .* not found' config.value.mkiffalse ./types-anything/mk-mods.nix
checkConfigOutput '^{}$' config.value.mkiftrue ./types-anything/mk-mods.nix
checkConfigOutput '^1$' config.value.mkdefault ./types-anything/mk-mods.nix
checkConfigOutput '^{}$' config.value.mkmerge ./types-anything/mk-mods.nix
checkConfigOutput '^true$' config.value.mkbefore ./types-anything/mk-mods.nix
checkConfigOutput '^1$' config.value.nested.foo ./types-anything/mk-mods.nix
checkConfigOutput '^"baz"$' config.value.nested.bar.baz ./types-anything/mk-mods.nix
## types.functionTo
checkConfigOutput '^"input is input"$' config.result ./functionTo/trivial.nix
checkConfigOutput '^"a b"$' config.result ./functionTo/merging-list.nix
checkConfigError 'A definition for option .fun.<function body>. is not of type .string.. Definition values:\n\s*- In .*wrong-type.nix' config.result ./functionTo/wrong-type.nix
checkConfigOutput '^"b a"$' config.result ./functionTo/list-order.nix
checkConfigOutput '^"a c"$' config.result ./functionTo/merging-attrs.nix
checkConfigOutput '^"a bee"$' config.result ./functionTo/submodule-options.nix
checkConfigOutput '^"fun.<function body>.a fun.<function body>.b"$' config.optionsResult ./functionTo/submodule-options.nix
# moduleType
checkConfigOutput '^"a b"$' config.resultFoo ./declare-variants.nix ./define-variant.nix
checkConfigOutput '^"a b y z"$' config.resultFooBar ./declare-variants.nix ./define-variant.nix
checkConfigOutput '^"a b c"$' config.resultFooFoo ./declare-variants.nix ./define-variant.nix
## emptyValue's
checkConfigOutput "\[\]" config.list.a ./emptyValues.nix
checkConfigOutput "{}" config.attrs.a ./emptyValues.nix
checkConfigOutput "null" config.null.a ./emptyValues.nix
checkConfigOutput "{}" config.submodule.a ./emptyValues.nix
# These types don't have empty values
checkConfigError 'The option .int.a. was accessed but has no value defined. Try setting the option.' config.int.a ./emptyValues.nix
checkConfigError 'The option .nonEmptyList.a. was accessed but has no value defined. Try setting the option.' config.nonEmptyList.a ./emptyValues.nix
# types.unique
# requires a single definition
checkConfigError 'The option .examples\.merged. is defined multiple times while it.s expected to be unique' config.examples.merged.a ./types-unique.nix
# user message is printed
checkConfigError 'We require a single definition, because seeing the whole value at once helps us maintain critical invariants of our system.' config.examples.merged.a ./types-unique.nix
# let the inner merge function check the values (on demand)
checkConfigError 'A definition for option .examples\.badLazyType\.a. is not of type .string.' config.examples.badLazyType.a ./types-unique.nix
# overriding still works (unlike option uniqueness)
checkConfigOutput '^"bee"$' config.examples.override.b ./types-unique.nix
## types.raw
checkConfigOutput '^true$' config.unprocessedNestingEvaluates.success ./raw.nix
checkConfigOutput "10" config.processedToplevel ./raw.nix
checkConfigError "The option .multiple. is defined multiple times" config.multiple ./raw.nix
checkConfigOutput "bar" config.priorities ./raw.nix
## Option collision
checkConfigError \
'The option .set. in module .*/declare-set.nix. would be a parent of the following options, but its type .attribute set of signed integer. does not support nested options.\n\s*- option[(]s[)] with prefix .set.enable. in module .*/declare-enable-nested.nix.' \
config.set \
./declare-set.nix ./declare-enable-nested.nix
# Options: accidental use of an option-type instead of option (or other tagged type; unlikely)
checkConfigError 'In module .*/options-type-error-typical.nix: expected an option declaration at option path .result. but got an attribute set with type option-type' config.result ./options-type-error-typical.nix
checkConfigError 'In module .*/options-type-error-typical-nested.nix: expected an option declaration at option path .result.here. but got an attribute set with type option-type' config.result.here ./options-type-error-typical-nested.nix
checkConfigError 'In module .*/options-type-error-configuration.nix: expected an option declaration at option path .result. but got an attribute set with type configuration' config.result ./options-type-error-configuration.nix
# Check that that merging of option collisions doesn't depend on type being set
checkConfigError 'The option .group..*would be a parent of the following options, but its type .<no description>. does not support nested options.\n\s*- option.s. with prefix .group.enable..*' config.group.enable ./merge-typeless-option.nix
# Test that types.optionType merges types correctly
checkConfigOutput '^10$' config.theOption.int ./optionTypeMerging.nix
checkConfigOutput '^"hello"$' config.theOption.str ./optionTypeMerging.nix
# Test that types.optionType correctly annotates option locations
checkConfigError 'The option .theOption.nested. in .other.nix. is already declared in .optionTypeFile.nix.' config.theOption.nested ./optionTypeFile.nix
# Test that types.optionType leaves types untouched as long as they don't need to be merged
checkConfigOutput 'ok' config.freeformItems.foo.bar ./adhoc-freeformType-survives-type-merge.nix
# Test that specifying both functor.wrapped and functor.payload isn't allowed
checkConfigError 'Type foo defines both `functor.payload` and `functor.wrapped` at the same time, which is not supported.' config.result ./default-type-merge-both.nix
# Anonymous submodules don't get nixed by import resolution/deduplication
# because of an `extendModules` bug, issue 168767.
checkConfigOutput '^1$' config.sub.specialisation.value ./extendModules-168767-imports.nix
# Class checks, evalModules
checkConfigOutput '^{}$' config.ok.config ./class-check.nix
checkConfigOutput '"nixos"' config.ok.class ./class-check.nix
checkConfigError 'The module `.*/module-class-is-darwin.nix`.*?expects class "nixos".' config.fail.config ./class-check.nix
checkConfigError 'The module `foo.nix#darwinModules.default`.*?expects class "nixos".' config.fail-anon.config ./class-check.nix
# Class checks, submoduleWith
checkConfigOutput '^{}$' config.sub.nixosOk ./class-check.nix
checkConfigError 'The module `.*/module-class-is-darwin.nix`.*?expects class "nixos".' config.sub.nixosFail.config ./class-check.nix
# submoduleWith type merge with different class
checkConfigError 'A submoduleWith option is declared multiple times with conflicting class values "darwin" and "nixos".' config.sub.mergeFail.config ./class-check.nix
# _type check
checkConfigError 'Expected a module, but found a value of type .*"flake".*, while trying to load a module into .*/module-imports-_type-check.nix' config.ok.config ./module-imports-_type-check.nix
checkConfigOutput '^true$' config.enable ./declare-enable.nix ./define-enable-with-top-level-mkIf.nix
checkConfigError 'Expected a module, but found a value of type .*"configuration".*, while trying to load a module into .*/import-configuration.nix.' config ./import-configuration.nix
checkConfigError 'please only import the modules that make up the configuration' config ./import-configuration.nix
checkConfigError 'Expected a module, but found a value of type "configuration", while trying to load a module into .*/import-error-submodule.nix, while trying to load a module into .*foo.*\.' config.foo ./import-error-submodule.nix
# doRename works when `warnings` does not exist.
checkConfigOutput '^1234$' config.c.d.e ./doRename-basic.nix
# doRename adds a warning.
checkConfigOutput '^"The option `a\.b. defined in `.*/doRename-warnings\.nix. has been renamed to `c\.d\.e.\."$' \
config.result \
./doRename-warnings.nix
checkConfigOutput "^true$" config.result ./doRename-condition.nix ./doRename-condition-enable.nix
checkConfigOutput "^true$" config.result ./doRename-condition.nix ./doRename-condition-no-enable.nix
checkConfigOutput "^true$" config.result ./doRename-condition.nix ./doRename-condition-migrated.nix
# Anonymous modules get deduplicated by key
checkConfigOutput '^"pear"$' config.once.raw ./merge-module-with-key.nix
checkConfigOutput '^"pear\\npear"$' config.twice.raw ./merge-module-with-key.nix
# Declaration positions
# Line should be present for direct options
checkConfigOutput '^14$' options.imported.line14.declarationPositions.0.line ./declaration-positions.nix
checkConfigOutput '/declaration-positions.nix"$' options.imported.line14.declarationPositions.0.file ./declaration-positions.nix
# Generated options may not have line numbers but they will at least get the
# right file
checkConfigOutput '/declaration-positions.nix"$' options.generated.line22.declarationPositions.0.file ./declaration-positions.nix
checkConfigOutput '^null$' options.generated.line22.declarationPositions.0.line ./declaration-positions.nix
# Submodules don't break it
checkConfigOutput '^45$' config.submoduleLine38.submodDeclLine45.0.line ./declaration-positions.nix
checkConfigOutput '/declaration-positions.nix"$' config.submoduleLine38.submodDeclLine45.0.file ./declaration-positions.nix
# New options under freeform submodules get collected into the parent submodule
# (consistent with .declarations behaviour, but weird; notably appears in system.build)
checkConfigOutput '^38|27$' options.submoduleLine38.declarationPositions.0.line ./declaration-positions.nix
checkConfigOutput '^38|27$' options.submoduleLine38.declarationPositions.1.line ./declaration-positions.nix
# nested options work
checkConfigOutput '^34$' options.nested.nestedLine34.declarationPositions.0.line ./declaration-positions.nix
# types.pathWith { inStore = true; }
checkConfigOutput '".*/store/0lz9p8xhf89kb1c1kk6jxrzskaiygnlh-bash-5.2-p15.drv"' config.pathInStore.ok1 ./pathWith.nix
checkConfigOutput '".*/store/0fb3ykw9r5hpayd05sr0cizwadzq1d8q-bash-5.2-p15"' config.pathInStore.ok2 ./pathWith.nix
checkConfigOutput '".*/store/0fb3ykw9r5hpayd05sr0cizwadzq1d8q-bash-5.2-p15/bin/bash"' config.pathInStore.ok3 ./pathWith.nix
checkConfigError 'A definition for option .* is not of type .path in the Nix store.. Definition values:\n\s*- In .*: ""' config.pathInStore.bad1 ./pathWith.nix
checkConfigError 'A definition for option .* is not of type .path in the Nix store.. Definition values:\n\s*- In .*: ".*/store"' config.pathInStore.bad2 ./pathWith.nix
checkConfigError 'A definition for option .* is not of type .path in the Nix store.. Definition values:\n\s*- In .*: ".*/store/"' config.pathInStore.bad3 ./pathWith.nix
checkConfigError 'A definition for option .* is not of type .path in the Nix store.. Definition values:\n\s*- In .*: ".*/store/.links"' config.pathInStore.bad4 ./pathWith.nix
checkConfigError 'A definition for option .* is not of type .path in the Nix store.. Definition values:\n\s*- In .*: "/foo/bar"' config.pathInStore.bad5 ./pathWith.nix
# types.pathWith { inStore = false; }
checkConfigOutput '"/foo/bar"' config.pathNotInStore.ok1 ./pathWith.nix
checkConfigOutput '".*/store"' config.pathNotInStore.ok2 ./pathWith.nix
checkConfigOutput '".*/store/"' config.pathNotInStore.ok3 ./pathWith.nix
checkConfigOutput '""' config.pathNotInStore.ok4 ./pathWith.nix
checkConfigOutput '".*/store/.links"' config.pathNotInStore.ok5 ./pathWith.nix
checkConfigError 'A definition for option .* is not of type .path not in the Nix store.. Definition values:\n\s*- In .*: ".*/0lz9p8xhf89kb1c1kk6jxrzskaiygnlh-bash-5.2-p15.drv"' config.pathNotInStore.bad1 ./pathWith.nix
checkConfigError 'A definition for option .* is not of type .path not in the Nix store.. Definition values:\n\s*- In .*: ".*/0fb3ykw9r5hpayd05sr0cizwadzq1d8q-bash-5.2-p15"' config.pathNotInStore.bad2 ./pathWith.nix
checkConfigError 'A definition for option .* is not of type .path not in the Nix store.. Definition values:\n\s*- In .*: ".*/0fb3ykw9r5hpayd05sr0cizwadzq1d8q-bash-5.2-p15/bin/bash"' config.pathNotInStore.bad3 ./pathWith.nix
checkConfigError 'A definition for option .* is not of type .path not in the Nix store.. Definition values:\n\s*- In .*: .*/pathWith.nix' config.pathNotInStore.bad4 ./pathWith.nix
# types.pathWith { }
checkConfigOutput '"/this/is/absolute"' config.anyPath.ok1 ./pathWith.nix
checkConfigOutput '"./this/is/relative"' config.anyPath.ok2 ./pathWith.nix
checkConfigError 'A definition for option .anyPath.bad1. is not of type .path.' config.anyPath.bad1 ./pathWith.nix
# types.pathWith { absolute = true; }
checkConfigOutput '"/this/is/absolute"' config.absolutePathNotInStore.ok1 ./pathWith.nix
checkConfigError 'A definition for option .absolutePathNotInStore.bad1. is not of type .absolute path not in the Nix store.' config.absolutePathNotInStore.bad1 ./pathWith.nix
checkConfigError 'A definition for option .absolutePathNotInStore.bad2. is not of type .absolute path not in the Nix store.' config.absolutePathNotInStore.bad2 ./pathWith.nix
# types.pathWith failed type merge
checkConfigError 'The option .conflictingPathOptionType. in .*/pathWith.nix. is already declared in .*/pathWith.nix' config.conflictingPathOptionType ./pathWith.nix
# types.pathWith { inStore = true; absolute = false; }
checkConfigError 'In pathWith, inStore means the path must be absolute' config.impossiblePathOptionType ./pathWith.nix
# mkDefinition
# check that mkDefinition 'file' is printed in the error message
checkConfigError 'Cannot merge definitions.*\n\s*- In .file.*\n\s*- In .other.*' config.conflict ./mkDefinition.nix
checkConfigError 'A definition for option .viaOptionDefault. is not of type .boolean.*' config.viaOptionDefault ./mkDefinition.nix
checkConfigOutput '^true$' config.viaConfig ./mkDefinition.nix
checkConfigOutput '^true$' config.mkMerge ./mkDefinition.nix
checkConfigOutput '^true$' config.mkForce ./mkDefinition.nix
# specialArgs._class
checkConfigOutput '"nixos"' config.nixos.config.foo ./specialArgs-class.nix
checkConfigOutput '"bar"' config.conditionalImportAsNixos.config.foo ./specialArgs-class.nix
checkConfigError 'attribute .*bar.* not found' config.conditionalImportAsNixos.config.bar ./specialArgs-class.nix
checkConfigError 'attribute .*foo.* not found' config.conditionalImportAsDarwin.config.foo ./specialArgs-class.nix
checkConfigOutput '"foo"' config.conditionalImportAsDarwin.config.bar ./specialArgs-class.nix
checkConfigOutput '"nixos"' config.sub.nixos.foo ./specialArgs-class.nix
checkConfigOutput '"bar"' config.sub.conditionalImportAsNixos.foo ./specialArgs-class.nix
checkConfigError 'attribute .*bar.* not found' config.sub.conditionalImportAsNixos.bar ./specialArgs-class.nix
checkConfigError 'attribute .*foo.* not found' config.sub.conditionalImportAsDarwin.foo ./specialArgs-class.nix
checkConfigOutput '"foo"' config.sub.conditionalImportAsDarwin.bar ./specialArgs-class.nix
# Check that some types expose the 'valueMeta'
checkConfigOutput '\{\}' options.str.valueMeta ./types-valueMeta.nix
checkConfigOutput '["foo", "bar"]' config.attrsOfResult ./types-valueMeta.nix
checkConfigOutput '2' config.listOfResult ./types-valueMeta.nix
# Check that composed types expose the 'valueMeta'
# attrsOf submodule (also on merged options,types)
checkConfigOutput '42' options.attrsOfModule.valueMeta.attrs.foo.configuration.options.bar.value ./composed-types-valueMeta.nix
checkConfigOutput '42' options.mergedAttrsOfModule.valueMeta.attrs.foo.configuration.options.bar.value ./composed-types-valueMeta.nix
# listOf submodule (also on merged options,types)
checkConfigOutput '42' config.listResult ./composed-types-valueMeta.nix
checkConfigOutput '42' config.mergedListResult ./composed-types-valueMeta.nix
# Add check
checkConfigOutput '^0$' config.v1CheckedPass ./add-check.nix
checkConfigError 'A definition for option .* is not of type .signed integer.*' config.v1CheckedFail ./add-check.nix
checkConfigOutput '^true$' config.v2checkedPass ./add-check.nix
checkConfigError 'A definition for option .* is not of type .attribute set of signed integer.*' config.v2checkedFail ./add-check.nix
cat <<EOF
====== module tests ======
$pass Pass
$fail Fail
EOF
if [ "$fail" -ne 0 ]; then
exit 1
fi
exit 0

View File

@@ -0,0 +1,36 @@
(
{ lib, ... }:
let
inherit (lib) types mkOption;
inherit (types) addCheck int attrsOf;
# type with a v1 merge
v1Type = addCheck int (v: v == 0);
# type with a v2 merge
v2Type = addCheck (attrsOf int) (v: v ? foo);
in
{
options.v1CheckedPass = mkOption {
type = v1Type;
default = 0;
};
options.v1CheckedFail = mkOption {
type = v1Type;
default = 1;
};
options.v2checkedPass = mkOption {
type = v2Type;
default = {
foo = 1;
};
# plug the value to make test script regex simple
apply = v: v.foo == 1;
};
options.v2checkedFail = mkOption {
type = v2Type;
default = { };
apply = v: lib.deepSeq v v;
};
}
)

View File

@@ -0,0 +1,19 @@
{ lib, ... }:
{
options.dummy = lib.mkOption {
type = lib.types.anything;
default = { };
};
freeformType =
let
a = lib.types.attrsOf (lib.types.submodule { options.bar = lib.mkOption { }; });
in
# modifying types like this breaks type merging.
# This test makes sure that type merging is not performed when only a single declaration exists.
# Don't modify types in practice!
a
// {
merge = loc: defs: { freeformItems = a.merge loc defs; };
};
config.foo.bar = "ok";
}

View File

@@ -0,0 +1,64 @@
# This is a test to show that mkAliasOptionModule sets the priority correctly
# for aliased options.
#
# This test shows that an alias with a high priority is able to override
# a non-aliased option.
{ config, lib, ... }:
let
inherit (lib)
mkAliasOptionModule
mkForce
mkOption
types
;
in
{
options = {
# A simple boolean option that can be enabled or disabled.
enable = mkOption {
type = types.nullOr types.bool;
default = null;
example = true;
description = ''
Some descriptive text
'';
};
# mkAliasOptionModule sets warnings, so this has to be defined.
warnings = mkOption {
internal = true;
default = [ ];
type = types.listOf types.str;
example = [ "The `foo' service is deprecated and will go away soon!" ];
description = ''
This option allows modules to show warnings to users during
the evaluation of the system configuration.
'';
};
};
imports = [
# Create an alias for the "enable" option.
(mkAliasOptionModule [ "enableAlias" ] [ "enable" ])
# Disable the aliased option with a high priority so it
# should override the next import.
(
{ config, lib, ... }:
{
enableAlias = mkForce false;
}
)
# Enable the normal (non-aliased) option.
(
{ config, lib, ... }:
{
enable = true;
}
)
];
}

View File

@@ -0,0 +1,64 @@
# This is a test to show that mkAliasOptionModule sets the priority correctly
# for aliased options.
#
# This test shows that an alias with a low priority is able to be overridden
# with a non-aliased option.
{ config, lib, ... }:
let
inherit (lib)
mkAliasOptionModule
mkDefault
mkOption
types
;
in
{
options = {
# A simple boolean option that can be enabled or disabled.
enable = mkOption {
type = types.nullOr types.bool;
default = null;
example = true;
description = ''
Some descriptive text
'';
};
# mkAliasOptionModule sets warnings, so this has to be defined.
warnings = mkOption {
internal = true;
default = [ ];
type = types.listOf types.str;
example = [ "The `foo' service is deprecated and will go away soon!" ];
description = ''
This option allows modules to show warnings to users during
the evaluation of the system configuration.
'';
};
};
imports = [
# Create an alias for the "enable" option.
(mkAliasOptionModule [ "enableAlias" ] [ "enable" ])
# Disable the aliased option, but with a default (low) priority so it
# should be able to be overridden by the next import.
(
{ config, lib, ... }:
{
enableAlias = mkDefault false;
}
)
# Enable the normal (non-aliased) option.
(
{ config, lib, ... }:
{
enable = true;
}
)
];
}

View File

@@ -0,0 +1,3 @@
{ _class, ... }:
assert _class == "nixos";
{ }

View File

@@ -0,0 +1,8 @@
{ lib, config, ... }:
{
options.conditionalWorks = lib.mkOption {
default = !config.value ? foo;
};
config.value.foo = lib.mkIf false "should not be defined";
}

View File

@@ -0,0 +1,8 @@
{ lib, config, ... }:
{
options.isLazy = lib.mkOption {
default = !config.value ? foo;
};
config.value.bar = throw "is not lazy";
}

View File

@@ -0,0 +1,26 @@
{ lib, ... }:
{
options.value = lib.mkOption {
type = lib.types.lazyAttrsOf lib.types.boolByOr;
};
config.value = {
falseFalse = lib.mkMerge [
false
false
];
trueFalse = lib.mkMerge [
true
false
];
falseTrue = lib.mkMerge [
false
true
];
trueTrue = lib.mkMerge [
true
true
];
};
}

View File

@@ -0,0 +1,82 @@
{ lib, ... }:
{
options = {
sub = {
nixosOk = lib.mkOption {
type = lib.types.submoduleWith {
class = "nixos";
modules = [
./assert-module-class-is-nixos.nix
];
};
};
# Same but will have bad definition
nixosFail = lib.mkOption {
type = lib.types.submoduleWith {
class = "nixos";
modules = [ ];
};
};
mergeFail = lib.mkOption {
type = lib.types.submoduleWith {
class = "nixos";
modules = [ ];
};
default = { };
};
};
};
imports = [
{
options = {
sub = {
mergeFail = lib.mkOption {
type = lib.types.submoduleWith {
class = "darwin";
modules = [ ];
};
};
};
};
}
];
config = {
_module.freeformType = lib.types.anything;
ok = lib.evalModules {
class = "nixos";
modules = [
./module-class-is-nixos.nix
./assert-module-class-is-nixos.nix
];
};
fail = lib.evalModules {
class = "nixos";
modules = [
./module-class-is-nixos.nix
./module-class-is-darwin.nix
];
};
fail-anon = lib.evalModules {
class = "nixos";
modules = [
./module-class-is-nixos.nix
{
_file = "foo.nix#darwinModules.default";
_class = "darwin";
config = { };
imports = [ ];
}
];
};
sub.nixosOk = {
_class = "nixos";
};
sub.nixosFail = {
imports = [ ./module-class-is-darwin.nix ];
};
};
}

View File

@@ -0,0 +1,75 @@
{ lib, ... }:
let
inherit (lib) types mkOption;
attrsOfModule = mkOption {
type = types.attrsOf (
types.submodule {
options.bar = mkOption {
type = types.int;
};
}
);
};
listOfModule = mkOption {
type = types.listOf (
types.submodule {
options.bar = mkOption {
type = types.int;
};
}
);
};
in
{
imports = [
# Module A
{
options.attrsOfModule = attrsOfModule;
options.mergedAttrsOfModule = attrsOfModule;
options.listOfModule = listOfModule;
options.mergedListOfModule = listOfModule;
}
# Module B
{
options.mergedAttrsOfModule = attrsOfModule;
options.mergedListOfModule = listOfModule;
}
# Values
# It is important that the value is defined in a separate module
# Without valueMeta the actual value and sub-options wouldn't be accessible via:
# options.attrsOfModule.type.getSubOptions
{
attrsOfModule = {
foo.bar = 42;
};
mergedAttrsOfModule = {
foo.bar = 42;
};
}
(
{ options, ... }:
{
config.listOfModule = [
{
bar = 42;
}
];
config.mergedListOfModule = [
{
bar = 42;
}
];
# Result options to expose the list module to bash as plain attribute path
options.listResult = mkOption {
default = (builtins.head options.listOfModule.valueMeta.list).configuration.options.bar.value;
};
options.mergedListResult = mkOption {
default = (builtins.head options.mergedListOfModule.valueMeta.list).configuration.options.bar.value;
};
}
)
];
}

View File

@@ -0,0 +1,57 @@
{ lib, options, ... }:
let
discardPositions = lib.mapAttrs (k: v: v);
in
# unsafeGetAttrPos is unspecified best-effort behavior, so we only want to consider this test on an evaluator that satisfies some basic assumptions about this function.
assert builtins.unsafeGetAttrPos "a" { a = true; } != null;
assert
builtins.unsafeGetAttrPos "a" (discardPositions {
a = true;
}) == null;
{
imports = [
{
options.imported.line14 = lib.mkOption {
type = lib.types.int;
};
# Simulates various patterns of generating modules such as
# programs.firefox.nativeMessagingHosts.ff2mpv. We don't expect to get
# line numbers for these, but we can fall back on knowing the file.
options.generated = discardPositions {
line22 = lib.mkOption {
type = lib.types.int;
};
};
options.submoduleLine38.extraOptLine27 = lib.mkOption {
default = 1;
type = lib.types.int;
};
}
];
options.nested.nestedLine34 = lib.mkOption {
type = lib.types.int;
};
options.submoduleLine38 = lib.mkOption {
default = { };
type = lib.types.submoduleWith {
modules = [
(
{ options, ... }:
{
options.submodDeclLine45 = lib.mkOption { };
}
)
{ freeformType = with lib.types; lazyAttrsOf (uniq unspecified); }
];
};
};
config = {
submoduleLine38.submodDeclLine45 =
(options.submoduleLine38.type.getSubOptions [ ]).submodDeclLine45.declarationPositions;
};
}

View File

@@ -0,0 +1,13 @@
{ lib, ... }:
let
deathtrapArgs = lib.mapAttrs (
k: _: throw "The module system is too strict, accessing an unused option's ${k} mkOption-attribute."
) (lib.functionArgs lib.mkOption);
in
{
options.value = lib.mkOption {
type = lib.types.attrsOf lib.types.str;
default = { };
};
options.testing-laziness-so-don't-read-me = lib.mkOption deathtrapArgs;
}

View File

@@ -0,0 +1,31 @@
{ lib, ... }:
let
submod =
{ ... }:
{
options = {
enable = lib.mkOption {
default = false;
example = true;
type = lib.types.bool;
description = ''
Some descriptive text
'';
};
};
};
in
{
options = {
attrsOfSub = lib.mkOption {
default = { };
example = { };
type = lib.types.attrsOf (lib.types.submodule [ submod ]);
description = ''
Some descriptive text
'';
};
};
}

View File

@@ -0,0 +1,10 @@
{ lib, ... }:
let
inherit (lib) mkOption types;
in
{
options.bare-submodule.deep = mkOption {
type = types.int;
default = 2;
};
}

View File

@@ -0,0 +1,10 @@
{ lib, ... }:
let
inherit (lib) mkOption types;
in
{
options.bare-submodule.deep = mkOption {
type = types.int;
default = 2;
};
}

View File

@@ -0,0 +1,19 @@
{ config, lib, ... }:
let
inherit (lib) mkOption types;
in
{
options.bare-submodule = mkOption {
type = types.submoduleWith {
shorthandOnlyDefinesConfig = config.shorthandOnlyDefinesConfig;
modules = [
{
options.nested = mkOption {
type = types.int;
default = 1;
};
}
];
};
};
}

View File

@@ -0,0 +1,18 @@
{ config, lib, ... }:
let
inherit (lib) mkOption types;
in
{
options.bare-submodule = mkOption {
type = types.submoduleWith {
modules = [ ];
shorthandOnlyDefinesConfig = config.shorthandOnlyDefinesConfig;
};
default = { };
};
# config-dependent options: won't recommend, but useful for making this test parameterized
options.shorthandOnlyDefinesConfig = mkOption {
default = false;
};
}

View File

@@ -0,0 +1,9 @@
{ lib, ... }:
{
options = {
value = lib.mkOption {
type = lib.types.coercedTo lib.types.int toString lib.types.str;
};
};
}

View File

@@ -0,0 +1,10 @@
{ lib, ... }:
{
options = {
value = lib.mkOption {
default = "12";
type = lib.types.coercedTo lib.types.str lib.toInt lib.types.ints.s8;
};
};
}

View File

@@ -0,0 +1,10 @@
{ lib, ... }:
{
options = {
value = lib.mkOption {
default = 42;
type = lib.types.coercedTo lib.types.int toString lib.types.str;
};
};
}

View File

@@ -0,0 +1,6 @@
{ lib, ... }:
{
options.value = lib.mkOption {
type = lib.types.either lib.types.int lib.types.str;
};
}

View File

@@ -0,0 +1,14 @@
{ lib, ... }:
{
options.set = {
enable = lib.mkOption {
default = false;
example = true;
type = lib.types.bool;
description = ''
Some descriptive text
'';
};
};
}

View File

@@ -0,0 +1,14 @@
{ lib, ... }:
{
options = {
enable = lib.mkOption {
default = false;
example = true;
type = lib.types.bool;
description = ''
Some descriptive text
'';
};
};
}

View File

@@ -0,0 +1,9 @@
{ lib, ... }:
{
options = {
value = lib.mkOption {
type = lib.types.ints.between (-21) 43;
};
};
}

View File

@@ -0,0 +1,9 @@
{ lib, ... }:
{
options.set = {
value = lib.mkOption {
type = lib.types.ints.positive;
};
};
}

View File

@@ -0,0 +1,9 @@
{ lib, ... }:
{
options = {
value = lib.mkOption {
type = lib.types.ints.positive;
};
};
}

View File

@@ -0,0 +1,9 @@
{ lib, ... }:
{
options = {
value = lib.mkOption {
type = lib.types.ints.unsigned;
};
};
}

View File

@@ -0,0 +1,7 @@
{ lib, ... }:
{
options.value = lib.mkOption {
type = lib.types.lazyAttrsOf (lib.types.str // { emptyValue.value = "empty"; });
default = { };
};
}

View File

@@ -0,0 +1,75 @@
{ lib, ... }:
let
pkgs.hello = {
type = "derivation";
pname = "hello";
};
in
{
options = {
package = lib.mkPackageOption pkgs "hello" { };
namedPackage = lib.mkPackageOption pkgs "Hello" {
default = [ "hello" ];
};
namedPackageSingletonDefault = lib.mkPackageOption pkgs "Hello" {
default = "hello";
};
pathPackage = lib.mkPackageOption pkgs [ "hello" ] { };
packageWithExample = lib.mkPackageOption pkgs "hello" {
example = "pkgs.hello.override { stdenv = pkgs.clangStdenv; }";
};
packageWithPathExample = lib.mkPackageOption pkgs "hello" {
example = [ "hello" ];
};
packageWithExtraDescription = lib.mkPackageOption pkgs "hello" {
extraDescription = "Example extra description.";
};
undefinedPackage = lib.mkPackageOption pkgs "hello" {
default = null;
};
nullablePackage = lib.mkPackageOption pkgs "hello" {
nullable = true;
default = null;
};
nullablePackageWithDefault = lib.mkPackageOption pkgs "hello" {
nullable = true;
};
packageWithPkgsText = lib.mkPackageOption pkgs "hello" {
pkgsText = "myPkgs";
};
packageFromOtherSet =
let
myPkgs = {
hello = pkgs.hello // {
pname = "hello-other";
};
};
in
lib.mkPackageOption myPkgs "hello" { };
packageInvalidIdentifier =
let
myPkgs."123"."with\"quote" = { inherit (pkgs) hello; };
in
lib.mkPackageOption myPkgs [ "123" "with\"quote" "hello" ] { };
packageInvalidIdentifierExample = lib.mkPackageOption pkgs "hello" {
example = [
"123"
"with\"quote"
"hello"
];
};
};
}

View File

@@ -0,0 +1,10 @@
{ lib, ... }:
{
options.value = lib.mkOption {
type = lib.types.oneOf [
lib.types.int
(lib.types.listOf lib.types.int)
lib.types.str
];
};
}

View File

@@ -0,0 +1,14 @@
{ lib, ... }:
{
options.set = lib.mkOption {
default = { };
example = {
a = 1;
};
type = lib.types.attrsOf lib.types.int;
description = ''
Some descriptive text
'';
};
}

View File

@@ -0,0 +1,35 @@
{ lib, ... }:
{
options.submodule = lib.mkOption {
inherit
(lib.evalModules {
modules = [
{
options.inner = lib.mkOption {
type = lib.types.bool;
default = false;
};
}
];
})
type
;
default = { };
};
config.submodule = lib.mkMerge [
(
{ lib, ... }:
{
options.outer = lib.mkOption {
type = lib.types.bool;
default = false;
};
}
)
{
inner = true;
outer = true;
}
];
}

View File

@@ -0,0 +1,32 @@
{ lib, ... }:
{
options.submodule = lib.mkOption {
type = lib.types.submoduleWith {
modules = [
{
options.inner = lib.mkOption {
type = lib.types.bool;
default = false;
};
}
];
};
default = { };
};
config.submodule = lib.mkMerge [
(
{ lib, ... }:
{
options.outer = lib.mkOption {
type = lib.types.bool;
default = false;
};
}
)
{
inner = true;
outer = true;
}
];
}

View File

@@ -0,0 +1,15 @@
{ lib, ... }:
let
sub.options.config = lib.mkOption {
type = lib.types.bool;
default = false;
};
in
{
options.submodule = lib.mkOption {
type = lib.types.submoduleWith {
modules = [ sub ];
};
default = { };
};
}

View File

@@ -0,0 +1,13 @@
{ lib, ... }:
{
options.submodule = lib.mkOption {
type = lib.types.submoduleWith {
modules = [
./declare-enable.nix
];
};
default = { };
};
config.submodule = ./define-enable.nix;
}

View File

@@ -0,0 +1,16 @@
{ lib, ... }:
let
sub.options.config = lib.mkOption {
type = lib.types.bool;
default = false;
};
in
{
options.submodule = lib.mkOption {
type = lib.types.submoduleWith {
modules = [ sub ];
shorthandOnlyDefinesConfig = true;
};
default = { };
};
}

View File

@@ -0,0 +1,21 @@
{ lib, ... }:
{
options.submodule = lib.mkOption {
type = lib.types.submoduleWith {
modules = [
(
{ lib, ... }:
{
options.foo = lib.mkOption {
default = lib.foo;
};
}
)
];
specialArgs.lib = lib // {
foo = "foo";
};
};
default = { };
};
}

View File

@@ -0,0 +1,10 @@
{ lib, moduleType, ... }:
let
inherit (lib) mkOption types;
in
{
options.variants = mkOption {
type = types.lazyAttrsOf moduleType;
default = { };
};
}

View File

@@ -0,0 +1,28 @@
{ lib, options, ... }:
let
foo = lib.mkOptionType {
name = "foo";
functor = lib.types.defaultFunctor "foo" // {
wrapped = lib.types.int;
payload = 10;
};
};
in
{
imports = [
{
options.foo = lib.mkOption {
type = foo;
};
}
{
options.foo = lib.mkOption {
type = foo;
};
}
];
options.result = lib.mkOption {
default = builtins.seq options.foo null;
};
}

View File

@@ -0,0 +1,15 @@
{
lib ? import ../..,
modules ? [ ],
}:
{
inherit
(lib.evalModules {
inherit modules;
specialArgs.modulesPath = ./.;
})
config
options
;
}

View File

@@ -0,0 +1,33 @@
{ config, lib, ... }:
let
inherit (lib)
types
mkOption
setDefaultModuleLocation
evalModules
;
inherit (types)
deferredModule
lazyAttrsOf
submodule
str
raw
enum
;
in
{
options = {
deferred = mkOption {
type = deferredModule;
};
result = mkOption {
default = (evalModules { modules = [ config.deferred ]; }).config.result;
};
};
config = {
deferred =
{ ... }:
# this should be an attrset, so this fails
true;
};
}

View File

@@ -0,0 +1,77 @@
{ lib, ... }:
let
inherit (lib) types mkOption setDefaultModuleLocation;
inherit (types)
deferredModule
lazyAttrsOf
submodule
str
raw
enum
;
in
{
imports = [
# generic module, declaring submodules:
# - nodes.<name>
# - default
# where all nodes include the default
(
{ config, ... }:
{
_file = "generic.nix";
options.nodes = mkOption {
type = lazyAttrsOf (submodule {
imports = [ config.default ];
});
default = { };
};
options.default = mkOption {
type = deferredModule;
default = { };
description = ''
Module that is included in all nodes.
'';
};
}
)
{
_file = "default-1.nix";
default =
{ config, ... }:
{
options.settingsDict = lib.mkOption {
type = lazyAttrsOf str;
default = { };
};
options.bottom = lib.mkOption { type = enum [ ]; };
};
}
{
_file = "default-a-is-b.nix";
default = ./define-settingsDict-a-is-b.nix;
}
{
_file = "nodes-foo.nix";
nodes.foo.settingsDict.b = "beta";
}
{
_file = "the-file-that-contains-the-bad-config.nix";
default.bottom = "bogus";
}
{
_file = "nodes-foo-c-is-a.nix";
nodes.foo =
{ config, ... }:
{
settingsDict.c = config.settingsDict.a;
};
}
];
}

View File

@@ -0,0 +1,7 @@
{ ... }:
{
config = {
_module.args.custom = true;
};
}

View File

@@ -0,0 +1,3 @@
{
attrsOfSub.bar.enable = true;
}

View File

@@ -0,0 +1,3 @@
{
attrsOfSub.bar = { };
}

View File

@@ -0,0 +1,5 @@
{ lib, ... }:
{
attrsOfSub.foo.enable = lib.mkForce false;
}

View File

@@ -0,0 +1,5 @@
{ config, lib, ... }:
{
attrsOfSub.foo.enable = lib.mkIf config.enable true;
}

View File

@@ -0,0 +1,3 @@
{
attrsOfSub.foo.enable = true;
}

View File

@@ -0,0 +1,7 @@
{ lib, ... }:
{
attrsOfSub.foo = lib.mkForce {
enable = false;
};
}

View File

@@ -0,0 +1,7 @@
{ config, lib, ... }:
{
attrsOfSub.foo = lib.mkIf config.enable {
enable = true;
};
}

View File

@@ -0,0 +1,3 @@
{
attrsOfSub.foo = { };
}

View File

@@ -0,0 +1,7 @@
{ lib, ... }:
{
attrsOfSub = lib.mkForce {
foo.enable = false;
};
}

View File

@@ -0,0 +1,7 @@
{ config, lib, ... }:
{
attrsOfSub = lib.mkIf config.enable {
foo.enable = true;
};
}

View File

@@ -0,0 +1,4 @@
{
bare-submodule.nested = 42;
bare-submodule.deep = 420;
}

View File

@@ -0,0 +1,3 @@
{
config.enable = abort "oops";
}

View File

@@ -0,0 +1,5 @@
{ lib, ... }:
{
enable = lib.mkForce false;
}

View File

@@ -0,0 +1,3 @@
{
config.enable = throw "oops";
}

View File

@@ -0,0 +1,7 @@
{ custom, ... }:
{
config = {
enable = custom;
};
}

View File

@@ -0,0 +1,5 @@
{ lib, ... }:
# I think this might occur more realistically in a submodule
{
imports = [ (lib.mkIf true { enable = true; }) ];
}

View File

@@ -0,0 +1,3 @@
{
enable = true;
}

View File

@@ -0,0 +1,5 @@
{ lib, ... }:
lib.mkForce {
attrsOfSub.foo.enable = false;
}

View File

@@ -0,0 +1,5 @@
{ lib, ... }:
lib.mkForce {
enable = false;
}

View File

@@ -0,0 +1,24 @@
{ config, ... }:
{
class = {
"just" = "data";
};
a = "one";
b = "two";
meta = "meta";
_module.args.result =
let
r = removeAttrs config [ "_module" ];
in
builtins.trace (builtins.deepSeq r r) (
r == {
a = "one";
b = "two";
class = {
"just" = "data";
};
meta = "meta";
}
);
}

View File

@@ -0,0 +1,5 @@
{ config, lib, ... }:
lib.mkIf config.enable {
attrsOfSub.foo.enable = true;
}

View File

@@ -0,0 +1,3 @@
{
_module.check = false;
}

View File

@@ -0,0 +1,16 @@
{ lib, options, ... }:
# Some modules may be distributed separately and need to adapt to other modules
# that are distributed and versioned separately.
{
# Always defined, but the value depends on the presence of an option.
config.set = {
value = if options ? set.enable then 360 else 7;
}
# Only define if possible.
// lib.optionalAttrs (options ? set.enable) {
enable = true;
};
}

View File

@@ -0,0 +1,16 @@
{ lib, options, ... }:
# Some modules may be distributed separately and need to adapt to other modules
# that are distributed and versioned separately.
{
# Always defined, but the value depends on the presence of an option.
config = {
value = if options ? enable then 360 else 7;
}
# Only define if possible.
// lib.optionalAttrs (options ? enable) {
enable = true;
};
}

View File

@@ -0,0 +1,4 @@
{ config, ... }:
{
settingsDict.a = config.settingsDict.b;
}

View File

@@ -0,0 +1 @@
{ shorthandOnlyDefinesConfig = true; }

View File

@@ -0,0 +1,3 @@
{
submodule.config.config = true;
}

View File

@@ -0,0 +1,3 @@
{
submodule.config = true;
}

View File

@@ -0,0 +1,3 @@
{
value = -23;
}

View File

@@ -0,0 +1,3 @@
{
value = 42;
}

View File

@@ -0,0 +1,3 @@
{
value = 0;
}

View File

@@ -0,0 +1,3 @@
{
value = [ ];
}

View File

@@ -0,0 +1,3 @@
{
value = "foobar";
}

View File

@@ -0,0 +1,3 @@
{
value = "1000";
}

View File

@@ -0,0 +1,15 @@
{ lib, ... }:
{
imports = [
{
value = lib.mkDefault "def";
}
];
value = lib.mkMerge [
(lib.mkIf false "nope")
"yes"
];
}

View File

@@ -0,0 +1,3 @@
{
value = "24";
}

View File

@@ -0,0 +1,27 @@
{ config, lib, ... }:
let
inherit (lib) types mkOption attrNames;
in
{
options = {
attrs = mkOption { type = types.attrsOf lib.types.int; };
result = mkOption { };
resultFoo = mkOption { };
resultFooBar = mkOption { };
resultFooFoo = mkOption { };
};
config = {
attrs.a = 1;
variants.foo.attrs.b = 1;
variants.bar.attrs.y = 1;
variants.foo.variants.bar.attrs.z = 1;
variants.foo.variants.foo.attrs.c = 3;
resultFoo = lib.concatMapStringsSep " " toString (attrNames config.variants.foo.attrs);
resultFooBar = lib.concatMapStringsSep " " toString (
attrNames config.variants.foo.variants.bar.attrs
);
resultFooFoo = lib.concatMapStringsSep " " toString (
attrNames config.variants.foo.variants.foo.attrs
);
};
}

View File

@@ -0,0 +1,93 @@
{ lib, ... }:
let
inherit (lib) types mkOption;
inherit (types)
# attrsOf uses attrsWith internally
attrsOf
listOf
unique
nullOr
functionTo
coercedTo
either
;
in
{
imports = [
# Module A
(
{ ... }:
{
options.attrsWith = mkOption {
type = attrsOf (listOf types.str);
};
options.mergedAttrsWith = mkOption {
type = attrsOf (listOf types.str);
};
options.listOf = mkOption {
type = listOf (listOf types.str);
};
options.mergedListOf = mkOption {
type = listOf (listOf types.str);
};
# unique
options.unique = mkOption {
type = unique { message = ""; } (listOf types.str);
};
options.mergedUnique = mkOption {
type = unique { message = ""; } (listOf types.str);
};
# nullOr
options.nullOr = mkOption {
type = nullOr (listOf types.str);
};
options.mergedNullOr = mkOption {
type = nullOr (listOf types.str);
};
# functionTo
options.functionTo = mkOption {
type = functionTo (listOf types.str);
};
options.mergedFunctionTo = mkOption {
type = functionTo (listOf types.str);
};
# coercedTo
# Note: coercedTo is a non-mergeable option-type
options.coercedTo = mkOption {
type = coercedTo (listOf types.str) lib.id (listOf types.str);
};
options.either = mkOption {
type = either (listOf types.str) (listOf types.str);
};
options.mergedEither = mkOption {
type = either (listOf types.str) (listOf types.str);
};
}
)
# Module B
(
{ ... }:
{
options.mergedAttrsWith = mkOption {
type = attrsOf (listOf types.str);
};
options.mergedListOf = mkOption {
type = listOf (listOf types.str);
};
options.mergedUnique = mkOption {
type = unique { message = ""; } (listOf types.str);
};
options.mergedNullOr = mkOption {
type = nullOr (listOf types.str);
};
options.mergedFunctionTo = mkOption {
type = functionTo (listOf types.str);
};
options.mergedEither = mkOption {
type = either (listOf types.str) (listOf types.str);
};
}
)
];
}

View File

@@ -0,0 +1,5 @@
{ ... }:
{
disabledModules = [ ./declare-enable.nix ];
}

View File

@@ -0,0 +1,5 @@
{ ... }:
{
disabledModules = [ (toString ./define-enable.nix) ];
}

View File

@@ -0,0 +1,5 @@
{ ... }:
{
disabledModules = [ ./define-enable.nix ];
}

View File

@@ -0,0 +1,8 @@
{ ... }:
{
disabledModules = [
"define-enable.nix"
"declare-enable.nix"
];
}

View File

@@ -0,0 +1,18 @@
{ lib, ... }:
let
inherit (lib) mkOption types;
moduleWithKey =
{ config, ... }:
{
config = {
enable = true;
};
};
in
{
imports = [
./declare-enable.nix
];
disabledModules = [ { } ];
}

View File

@@ -0,0 +1,34 @@
{ lib, ... }:
let
inherit (lib) mkOption types;
moduleWithKey = {
key = "disable-module-with-key.nix#moduleWithKey";
config = {
enable = true;
};
};
in
{
options = {
positive = mkOption {
type = types.submodule {
imports = [
./declare-enable.nix
moduleWithKey
];
};
default = { };
};
negative = mkOption {
type = types.submodule {
imports = [
./declare-enable.nix
moduleWithKey
];
disabledModules = [ moduleWithKey ];
};
default = { };
};
};
}

View File

@@ -0,0 +1,34 @@
{ lib, ... }:
let
inherit (lib) mkOption types;
moduleWithKey = {
key = 123;
config = {
enable = true;
};
};
in
{
options = {
positive = mkOption {
type = types.submodule {
imports = [
./declare-enable.nix
moduleWithKey
];
};
default = { };
};
negative = mkOption {
type = types.submodule {
imports = [
./declare-enable.nix
moduleWithKey
];
disabledModules = [ 123 ];
};
default = { };
};
};
}

View File

@@ -0,0 +1,5 @@
{
imports = [
../declare-enable.nix
];
}

View File

@@ -0,0 +1,7 @@
{
disabledModules = [
./bar.nix
];
}

View File

@@ -0,0 +1,7 @@
{
disabledModules = [
./foo.nix
];
}

View File

@@ -0,0 +1,5 @@
{
imports = [
../declare-enable.nix
];
}

Some files were not shown because too many files have changed in this diff Show More