{ config, lib, pkgs, ... }: with lib; let cfg = config.services.unison; pairOf = t: let list = types.addCheck (types.listOf t) (l: length l == 2); in list // { description = list.description + " of length 2"; }; pairOptions = { options = { stateDirectory = mkOption { type = types.path; default = "${config.xdg.dataHome}/unison"; defaultText = "$XDG_DATA_HOME/unison"; description = '' Unison state directory to use. ''; }; commandOptions = mkOption rec { type = with types; attrsOf (either str (listOf str)); apply = mergeAttrs default; default = { repeat = "watch"; sshcmd = "${pkgs.openssh}/bin/ssh"; ui = "text"; auto = "true"; batch = "true"; log = "false"; # don't log to file, handled by systemd }; description = '' Additional command line options as a dictionary to pass to the `unison` program. Use a list of strings to declare the same option multiple times. See {manpage}`unison(1)` for a list of available options. ''; }; roots = mkOption { type = pairOf types.str; example = literalExpression '' [ "/home/user/documents" "ssh://remote/documents" ] ''; description = '' Pair of roots to synchronise. ''; }; }; }; serialiseArg = key: val: concatStringsSep " " (forEach (toList val) (x: escapeShellArg "-${key}=${escape [ "=" ] x}")); serialiseArgs = args: concatStringsSep " " (mapAttrsToList serialiseArg args); unitName = name: "unison-pair-${name}"; makeDefs = gen: mapAttrs' (name: pairCfg: nameValuePair (unitName name) (gen name pairCfg)) cfg.pairs; in { meta.maintainers = with maintainers; [ pacien ]; options.services.unison = { enable = mkEnableOption "Unison synchronisation"; package = mkPackageOption pkgs "unison" { example = "pkgs.unison.override { enableX11 = false; }"; }; pairs = mkOption { type = with types; attrsOf (submodule pairOptions); default = { }; example = literalExpression '' { "my-documents" = { roots = [ "/home/user/documents" "ssh://remote/documents" ]; }; } ''; description = '' Unison root pairs to keep synchronised. ''; }; }; config = mkIf cfg.enable { assertions = [ (lib.hm.assertions.assertPlatform "services.unison" pkgs lib.platforms.linux) ]; systemd.user.services = makeDefs (name: pairCfg: { Unit.Description = "Unison pair sync (${name})"; Service = { CPUSchedulingPolicy = "idle"; IOSchedulingClass = "idle"; Environment = [ "UNISON='${toString pairCfg.stateDirectory}'" ]; ExecStart = '' ${cfg.package}/bin/unison \ ${serialiseArgs pairCfg.commandOptions} \ ${strings.concatMapStringsSep " " escapeShellArg pairCfg.roots} ''; }; }); systemd.user.timers = makeDefs (name: pairCfg: { Unit.Description = "Unison pair sync auto-restart (${name})"; Install.WantedBy = [ "timers.target" ]; Timer = { Unit = "${unitName name}.service"; OnActiveSec = 1; OnUnitInactiveSec = 60; }; }); }; }