mirror of
https://github.com/nix-community/home-manager.git
synced 2026-06-05 21:02:51 +00:00
225 lines
7.1 KiB
Nix
225 lines
7.1 KiB
Nix
{
|
|
lib,
|
|
config,
|
|
pkgs,
|
|
...
|
|
}:
|
|
let
|
|
inherit (lib) concatMapAttrs mkOption types;
|
|
|
|
portable-lib = import (pkgs.path + "/lib/services/lib.nix") { inherit lib; };
|
|
|
|
# Combine a service-name prefix with a unit name. The empty unit name
|
|
# `""` is the convention from nixpkgs' portable services for the
|
|
# service's *primary* unit (e.g. `services.foo.systemd.user.services.""`
|
|
# is the unit named `foo.service`). Sub-units use a hyphenated suffix.
|
|
dashed =
|
|
before: after:
|
|
if after == "" then
|
|
before
|
|
else if before == "" then
|
|
after
|
|
else
|
|
"${before}-${after}";
|
|
|
|
# Translate a NixOS-style systemd unit attrset (wantedBy, serviceConfig,
|
|
# unitConfig, environment, ...) into the section-based INI shape that
|
|
# Home Manager's `systemd.user.<unitType>` expects (Unit/Service/Install).
|
|
# Only the common keys are mapped; uncommon options can still be set
|
|
# explicitly via `unitConfig` / `serviceConfig` / `socketConfig`.
|
|
unitAttrKeys = [
|
|
"description"
|
|
"documentation"
|
|
"requires"
|
|
"wants"
|
|
"upholds"
|
|
"after"
|
|
"before"
|
|
"bindsTo"
|
|
"partOf"
|
|
"conflicts"
|
|
"requisite"
|
|
"onFailure"
|
|
"onSuccess"
|
|
];
|
|
pickSection =
|
|
keys: src:
|
|
lib.listToAttrs (
|
|
lib.concatMap (
|
|
k:
|
|
lib.optional (src ? ${k} && src.${k} != null && src.${k} != [ ]) {
|
|
name = lib.toSentenceCase k;
|
|
value = normalizeTargets src.${k};
|
|
}
|
|
) keys
|
|
);
|
|
envToList =
|
|
env: lib.mapAttrsToList (k: v: "${k}=${toString v}") (lib.filterAttrs (_: v: v != null) env);
|
|
normalizeTarget = t: if t == "multi-user.target" then "default.target" else t;
|
|
normalizeTargets = v: if lib.isList v then map normalizeTarget v else v;
|
|
installSection =
|
|
u:
|
|
lib.filterAttrs (_: v: v != [ ]) {
|
|
WantedBy = map normalizeTarget (u.wantedBy or [ ]);
|
|
RequiredBy = map normalizeTarget (u.requiredBy or [ ]);
|
|
};
|
|
toHmIni = unit: {
|
|
Unit = pickSection unitAttrKeys unit // (unit.unitConfig or { });
|
|
Service =
|
|
(unit.serviceConfig or { })
|
|
// lib.optionalAttrs (unit ? environment && unit.environment != { }) {
|
|
Environment = envToList unit.environment;
|
|
};
|
|
Install = installSection unit;
|
|
};
|
|
toHmIniSocket = sock: {
|
|
Unit = pickSection unitAttrKeys sock // (sock.unitConfig or { });
|
|
Socket =
|
|
(sock.socketConfig or { })
|
|
// lib.optionalAttrs (sock ? listenStreams && sock.listenStreams != [ ]) {
|
|
ListenStream = sock.listenStreams;
|
|
}
|
|
// lib.optionalAttrs (sock ? listenDatagrams && sock.listenDatagrams != [ ]) {
|
|
ListenDatagram = sock.listenDatagrams;
|
|
};
|
|
Install = installSection sock;
|
|
};
|
|
|
|
# Evaluate a deferredModule into attrs, then translate.
|
|
evalDeferred =
|
|
translator: unitModule:
|
|
translator
|
|
(lib.evalModules {
|
|
modules = [
|
|
(_: {
|
|
# Loose schema: NixOS-style unit attrs include nested sub-attrsets
|
|
# (e.g. `serviceConfig`) that need to merge across definitions.
|
|
freeformType =
|
|
with types;
|
|
attrsOf (oneOf [
|
|
(attrsOf raw)
|
|
(listOf raw)
|
|
raw
|
|
]);
|
|
})
|
|
unitModule
|
|
];
|
|
}).config;
|
|
|
|
# Collect the `source` store paths of a service's enabled `configData`
|
|
# entries. When `text` is set, the upstream `config-data-item.nix` module
|
|
# automatically derives `source` via `pkgs.writeText`, so `source` is
|
|
# always non-null for enabled entries.
|
|
configDataSources =
|
|
service:
|
|
lib.mapAttrsToList (_: cfg: cfg.source) (
|
|
lib.filterAttrs (_: cfg: cfg.enable) (service.configData or { })
|
|
);
|
|
|
|
makeUnits =
|
|
translator: unitType: prefix: service:
|
|
let
|
|
# Wire each service's `configData` sources into the primary unit's
|
|
# `X-Reload-Triggers` so `home-manager switch` restarts it whenever
|
|
# a config file changes. Preserves any triggers the unit already sets.
|
|
triggers = configDataSources service;
|
|
primaryTranslator =
|
|
if triggers == [ ] then
|
|
translator
|
|
else
|
|
(
|
|
unit:
|
|
let
|
|
ini = translator unit;
|
|
existing = lib.toList (ini.Unit.X-Reload-Triggers or [ ]);
|
|
in
|
|
ini
|
|
// {
|
|
Unit = ini.Unit // {
|
|
X-Reload-Triggers = lib.unique (existing ++ triggers);
|
|
};
|
|
}
|
|
);
|
|
in
|
|
concatMapAttrs (unitName: unitModule: {
|
|
"${dashed prefix unitName}" = evalDeferred (
|
|
if unitName == "" then primaryTranslator else translator
|
|
) unitModule;
|
|
}) service.systemd.${unitType}
|
|
// concatMapAttrs (
|
|
subName: subService: makeUnits translator unitType (dashed prefix subName) subService
|
|
) service.services;
|
|
|
|
# Lift each service's `configData` entries into `xdg.configFile` paths.
|
|
# Mirrors how `nixos/modules/system/service/systemd/system.nix` lifts
|
|
# `configData` to `environment.etc`.
|
|
makeConfigFiles =
|
|
prefix: service:
|
|
lib.mapAttrs' (_: cfg: {
|
|
name = "home-services/${prefix}/${cfg.name}";
|
|
value = lib.filterAttrs (_: v: v != null) {
|
|
source = cfg.source or null;
|
|
text = cfg.text or null;
|
|
inherit (cfg) enable;
|
|
};
|
|
}) (lib.filterAttrs (_: cfg: cfg.enable) (service.configData or { }))
|
|
// concatMapAttrs (
|
|
subName: subService: makeConfigFiles (dashed prefix subName) subService
|
|
) service.services;
|
|
|
|
modularServiceConfiguration = portable-lib.configure {
|
|
serviceManagerPkgs = pkgs;
|
|
extraRootModules = [
|
|
./service.nix
|
|
./config-data-path.nix
|
|
];
|
|
extraRootSpecialArgs = {
|
|
systemdPackage = pkgs.systemd;
|
|
nixpkgsPath = pkgs.path;
|
|
xdgConfigHome = config.xdg.configHome;
|
|
};
|
|
};
|
|
in
|
|
{
|
|
meta.maintainers = [ lib.maintainers.kiara ];
|
|
|
|
options.home.services = mkOption {
|
|
description = ''
|
|
Home Manager [modular services](https://nixos.org/manual/nixos/unstable/#modular-services).
|
|
|
|
Each entry is an abstract service that may declare a {option}`process.argv`
|
|
and Home Manager-style {option}`systemd.user.{services,sockets}` units
|
|
(INI section shape). Units are emitted under Home Manager's
|
|
{option}`systemd.user.services` (and friends) with the service name
|
|
as a prefix. Mirrors {option}`system.services` in NixOS.
|
|
'';
|
|
type = types.attrsOf modularServiceConfiguration.serviceSubmodule;
|
|
default = { };
|
|
visible = "shallow";
|
|
};
|
|
|
|
config = {
|
|
assertions = lib.concatLists (
|
|
lib.mapAttrsToList (
|
|
name: cfg: portable-lib.getAssertions [ "home" "services" name ] cfg
|
|
) config.home.services
|
|
);
|
|
|
|
warnings = lib.concatLists (
|
|
lib.mapAttrsToList (
|
|
name: cfg: portable-lib.getWarnings [ "home" "services" name ] cfg
|
|
) config.home.services
|
|
);
|
|
|
|
systemd.user.services = concatMapAttrs (
|
|
name: svc: makeUnits toHmIni "services" name svc
|
|
) config.home.services;
|
|
|
|
systemd.user.sockets = concatMapAttrs (
|
|
name: svc: makeUnits toHmIniSocket "sockets" name svc
|
|
) config.home.services;
|
|
|
|
xdg.configFile = concatMapAttrs (name: svc: makeConfigFiles name svc) config.home.services;
|
|
};
|
|
}
|