lib.modules.doRename: Add condition parameter

This is to support single-to-multi service migrations, so that the
`to` (e.g. `foos.""`) isn't defined unconditionally. See test cases.
This commit is contained in:
Robert Hensing 2024-02-01 20:15:26 +01:00
parent bc916a4dff
commit 29c7665003
6 changed files with 77 additions and 3 deletions

View file

@ -1256,7 +1256,7 @@ let
(opt.highestPrio or defaultOverridePriority) (opt.highestPrio or defaultOverridePriority)
(f opt.value); (f opt.value);
doRename = { from, to, visible, warn, use, withPriority ? true }: doRename = { from, to, visible, warn, use, withPriority ? true, condition ? true }:
{ config, options, ... }: { config, options, ... }:
let let
fromOpt = getAttrFromPath from options; fromOpt = getAttrFromPath from options;
@ -1272,7 +1272,7 @@ let
} // optionalAttrs (toType != null) { } // optionalAttrs (toType != null) {
type = toType; type = toType;
}); });
config = mkMerge [ config = mkIf condition (mkMerge [
(optionalAttrs (options ? warnings) { (optionalAttrs (options ? warnings) {
warnings = optional (warn && fromOpt.isDefined) warnings = optional (warn && fromOpt.isDefined)
"The option `${showOption from}' defined in ${showFiles fromOpt.files} has been renamed to `${showOption to}'."; "The option `${showOption from}' defined in ${showFiles fromOpt.files} has been renamed to `${showOption to}'.";
@ -1280,7 +1280,7 @@ let
(if withPriority (if withPriority
then mkAliasAndWrapDefsWithPriority (setAttrByPath to) fromOpt then mkAliasAndWrapDefsWithPriority (setAttrByPath to) fromOpt
else mkAliasAndWrapDefinitions (setAttrByPath to) fromOpt) else mkAliasAndWrapDefinitions (setAttrByPath to) fromOpt)
]; ]);
}; };
/* Use this function to import a JSON file as NixOS configuration. /* Use this function to import a JSON file as NixOS configuration.

View file

@ -465,6 +465,9 @@ checkConfigOutput '^1234$' config.c.d.e ./doRename-basic.nix
checkConfigOutput '^"The option `a\.b. defined in `.*/doRename-warnings\.nix. has been renamed to `c\.d\.e.\."$' \ checkConfigOutput '^"The option `a\.b. defined in `.*/doRename-warnings\.nix. has been renamed to `c\.d\.e.\."$' \
config.result \ config.result \
./doRename-warnings.nix ./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 # Anonymous modules get deduplicated by key
checkConfigOutput '^"pear"$' config.once.raw ./merge-module-with-key.nix checkConfigOutput '^"pear"$' config.once.raw ./merge-module-with-key.nix

View file

@ -0,0 +1,10 @@
{ config, lib, ... }:
{
config = {
services.foo.enable = true;
services.foo.bar = "baz";
result =
assert config.services.foos == { "" = { bar = "baz"; }; };
true;
};
}

View file

@ -0,0 +1,10 @@
{ config, lib, ... }:
{
config = {
services.foos."".bar = "baz";
result =
assert config.services.foos == { "" = { bar = "baz"; }; };
assert config.services.foo.bar == "baz";
true;
};
}

View file

@ -0,0 +1,9 @@
{ config, lib, options, ... }:
{
config = {
result =
assert config.services.foos == { };
assert ! options.services.foo.bar.isDefined;
true;
};
}

View file

@ -0,0 +1,42 @@
/*
Simulate a migration from a single-instance `services.foo` to a multi instance
`services.foos.<name>` module, where `name = ""` serves as the legacy /
compatibility instance.
- No instances must exist, unless one is defined in the multi-instance module,
or if the legacy enable option is set to true.
- The legacy instance options must be renamed to the new instance, if it exists.
The relevant scenarios are tested in separate files:
- ./doRename-condition-enable.nix
- ./doRename-condition-no-enable.nix
*/
{ config, lib, ... }:
let
inherit (lib) mkOption mkEnableOption types doRename;
in
{
options = {
services.foo.enable = mkEnableOption "foo";
services.foos = mkOption {
type = types.attrsOf (types.submodule {
options = {
bar = mkOption { type = types.str; };
};
});
default = { };
};
result = mkOption {};
};
imports = [
(doRename {
from = [ "services" "foo" "bar" ];
to = [ "services" "foos" "" "bar" ];
visible = true;
warn = false;
use = x: x;
withPriority = true;
condition = config.services.foo.enable;
})
];
}