diff --git a/modules/misc/news.nix b/modules/misc/news.nix index 29523c92..b2ce5296 100644 --- a/modules/misc/news.nix +++ b/modules/misc/news.nix @@ -1019,6 +1019,36 @@ in A new module is available: 'services.xcape'. ''; } + + { + time = "2019-04-11T22:50:10+00:00"; + condition = hostPlatform.isLinux; + message = '' + The type used for the systemd unit options under + + systemd.user.services, systemd.user.sockets, etc. + + has been changed to offer more robust merging of configurations. + + If you don't override values within systemd units then you are not + affected by this change. Unfortunately, if you do override unit values + you may encounter errors due to this change. + + In particular, if you get an error saying that a "unique option" is + "defined multiple times" then you need to use 'lib.mkForce'. For + example, + + systemd.user.services.foo.Service.ExecStart = "/foo/bar"; + + becomes + + systemd.user.services.foo.Service.ExecStart = lib.mkForce "/foo/bar"; + + We had to make this change because the old merging was causing too + many confusing situations for people. Apologies for potentially + breaking your configuration! + ''; + } ]; }; } diff --git a/modules/systemd.nix b/modules/systemd.nix index 051aee94..2a67bb31 100644 --- a/modules/systemd.nix +++ b/modules/systemd.nix @@ -58,9 +58,14 @@ let servicesStartTimeoutMs = builtins.toString cfg.servicesStartTimeoutMs; - attrsRecursivelyMerged = types.attrs // { - merge = loc: foldl' (res: def: recursiveUpdate res def.value) {}; - }; + unitType = unitKind: with types; + let + primitive = either bool (either int str); + in + attrsOf (attrsOf (attrsOf (either primitive (listOf primitive)))) + // { + description = "systemd ${unitKind} unit configuration"; + }; unitDescription = type: '' Definition of systemd per-user ${type} units. Attributes are @@ -78,6 +83,7 @@ let { Unit = { Description = "Example description"; + Documentation = [ "man:example(1)" "man:example(5)" ]; }; ${type} = { @@ -113,35 +119,35 @@ in services = mkOption { default = {}; - type = attrsRecursivelyMerged; + type = unitType "service"; description = unitDescription "service"; example = unitExample "Service"; }; sockets = mkOption { default = {}; - type = attrsRecursivelyMerged; + type = unitType "socket"; description = unitDescription "socket"; example = unitExample "Socket"; }; targets = mkOption { default = {}; - type = attrsRecursivelyMerged; + type = unitType "target"; description = unitDescription "target"; example = unitExample "Target"; }; timers = mkOption { default = {}; - type = attrsRecursivelyMerged; + type = unitType "timer"; description = unitDescription "timer"; example = unitExample "Timer"; }; paths = mkOption { default = {}; - type = attrsRecursivelyMerged; + type = unitType "path"; description = unitDescription "path"; example = unitExample "Path"; };