modules/programs: enforce (or rather document) a stricter schema

this should make it easier to switch to a different service manager
This commit is contained in:
Colin 2024-03-16 07:35:54 +00:00
parent 1b4a6207e2
commit c8e5edd61a
2 changed files with 123 additions and 17 deletions

View File

@ -255,18 +255,12 @@ let
'';
};
services = mkOption {
# see: <repo:nixos/nixpkgs:nixos/lib/utils.nix>
# type = utils.systemdUtils.types.services;
# map to listOf attrs so that we can allow multiple assigners to the same service
# w/o worrying about merging at this layer, and defer merging to modules/users instead.
type = types.attrsOf (types.coercedTo types.attrs (a: [ a ]) (types.listOf types.attrs));
type = types.attrsOf types.anything; # options.sane.users.value.type;
default = {};
description = ''
systemd services to define if this package is enabled.
currently only defines USER services -- acts as noop for root-enabled packages.
conventions are similar to `systemd.services` or `sane.users.<user>.services`.
the type at this level is obscured only to as to allow passthrough to `sane.users` w/ proper option merging
user services to define if this package is enabled.
acts as noop for root-enabled packages.
see `sane.users.<user>.services` for options;
'';
};
slowToBuild = mkOption {
@ -542,8 +536,7 @@ let
# conditionally persist relevant user dirs and create files
sane.users = lib.mapAttrs (user: en: lib.optionalAttrs (en && p.enabled) {
inherit (p) persist;
services = lib.mapAttrs (_: lib.mkMerge) p.services;
inherit (p) persist services;
environment = p.env;
fs = lib.mkMerge [
p.fs

View File

@ -4,6 +4,102 @@ let
sane-user-cfg = config.sane.user;
cfg = config.sane.users;
path-lib = sane-lib.path;
serviceType = with lib; types.submodule {
options = {
# these aoptions are mostly copied from systemd. could be improved.
description = mkOption {
type = types.str;
};
documentation = mkOption {
type = types.listOf types.str;
default = [];
description = ''
references and links for where to find documentation about this service.
'';
};
after = mkOption {
type = types.listOf types.str;
default = [];
};
bindsTo = mkOption {
type = types.listOf types.str;
default = [];
};
before = mkOption {
type = types.listOf types.str;
default = [];
};
requiredBy = mkOption {
type = types.listOf types.str;
default = [];
};
wantedBy = mkOption {
type = types.listOf types.str;
default = [];
};
wants = mkOption {
type = types.listOf types.str;
default = [];
};
script = mkOption {
type = types.nullOr types.lines;
default = null;
};
environment = mkOption {
type = types.attrsOf types.str;
default = {};
description = ''
environment variables to set within the service.
'';
};
serviceConfig.Type = mkOption {
type = types.enum [ "dbus" "oneshot" "simple" ];
};
serviceConfig.ExecStart = mkOption {
type = types.nullOr (types.coercedTo types.package toString types.str);
default = null;
};
serviceConfig.ExecStartPre = mkOption {
type = types.nullOr (types.coercedTo types.package toString types.str);
default = null;
};
serviceConfig.ExecStartPost = mkOption {
type = types.nullOr (types.coercedTo types.package toString types.str);
default = null;
};
serviceConfig.ExecStopPost = mkOption {
type = types.nullOr types.str;
default = null;
};
serviceConfig.BusName = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
name of the dbus name this service is expected to register.
only once the name is registered will the service be considered "active".
'';
};
serviceConfig.RemainAfterExit = mkOption {
type = types.bool;
default = false;
};
serviceConfig.Restart = mkOption {
type = types.nullOr (types.enum [ "always" "on-failure" ]);
default = null;
# N.B.: systemd doesn't allow always/on-failure for Type="oneshot" services
};
serviceConfig.RestartSec = mkOption {
type = types.str;
default = "20s";
};
unitConfig.ConditionEnvironment = mkOption {
type = types.nullOr types.str;
default = null;
};
};
};
userOptions = {
options = with lib; {
fs = mkOption {
@ -48,10 +144,10 @@ let
# type = utils.systemdUtils.types.services;
# `utils.systemdUtils.types.services` is nearly what we want, but remove `stage2ServiceConfig`,
# as we don't want to force a PATH for every service.
type = types.attrsOf (types.submodule [ utils.systemdUtils.unitOptions.stage2ServiceOptions utils.systemdUtils.lib.unitConfig ]);
type = types.attrsOf serviceType;
default = {};
description = ''
systemd user-services to define for this user.
services to define for this user.
populates files in ~/.config/systemd.
'';
};
@ -132,8 +228,25 @@ let
# see: <repo:nixos/nixpkgs:nixos/lib/utils.nix>
# see: <repo:nix-community/home-manager:modules/systemd.nix>
cleanName = utils.systemdUtils.lib.mkPathSafeName serviceName;
generatedUnit = utils.systemdUtils.lib.serviceToUnit serviceName (value // {
environment = lib.throwIf (value.path != []) "user service ${serviceName} specifies unsupported 'path' attribute (${builtins.toString value.path})" {
generatedUnit = utils.systemdUtils.lib.serviceToUnit serviceName {
inherit (value)
requiredBy
script
wantedBy
;
serviceConfig = lib.filterAttrs (_: v: v != null) value.serviceConfig;
unitConfig = {
inherit (value.unitConfig)
ConditionEnvironment
;
After = value.after;
Before = value.before;
BindsTo = value.bindsTo;
Description = value.description;
Documentation = value.documentation;
Wants = value.wants;
};
environment = {
# clear PATH to allow inheriting it from environment.
# otherwise, nixos would force it to `systemd.globalEnvironment.PATH`, which is mostly tools like sed/find/etc.
# clearing PATH here allows user services to inherit whatever PATH the graphical session sets
@ -145,7 +258,7 @@ let
# - <https://github.com/systemd/systemd/issues/1082>
PATH = null;
} // (value.environment or {});
});
};
#^ generatedUnit contains keys:
# - text
# - aliases (IGNORED)