aboutsummaryrefslogtreecommitdiff
path: root/home-manager/modules/services/unison.nix
blob: a9cf23fb66e930bd3e7ea94e498652f47e865f00 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
{ 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 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
          <literal>unison</literal> program.
          </para><para>
          See
          <citerefentry>
            <refentrytitle>unison</refentrytitle>
            <manvolnum>1</manvolnum>
          </citerefentry>
          for a list of available options.
        '';
      };

      roots = mkOption {
        type = pairOf types.str;
        example = literalExample ''
          [
            "/home/user/documents"
            "ssh://remote/documents"
          ]
        '';
        description = ''
          Pair of roots to synchronise.
        '';
      };
    };
  };

  serialiseArg = key: val: escapeShellArg "-${key}=${escape [ "=" ] val}";

  serialiseArgs = args: concatStringsSep " " (mapAttrsToList serialiseArg args);

  makeDefs = gen:
    mapAttrs'
    (name: pairCfg: nameValuePair "unison-pair-${name}" (gen name pairCfg))
    cfg.pairs;

in {
  meta.maintainers = with maintainers; [ pacien ];

  options.services.unison = {
    enable = mkEnableOption "Unison synchronisation";

    pairs = mkOption {
      type = with types; attrsOf (submodule pairOptions);
      default = { };
      example = literalExample ''
        {
          roots = [
            "/home/user/documents"
            "ssh://remote/documents"
          ];
        }
      '';
      description = ''
        Unison root pairs to keep synchronised.
      '';
    };
  };

  config = mkIf cfg.enable {
    systemd.user.services = makeDefs (name: pairCfg: {
      Unit = {
        Description = "Unison pair sync (${name})";
        # Retry forever, useful in case of network disruption.
        StartLimitIntervalSec = 0;
      };

      Service = {
        Restart = "always";
        RestartSec = 60;

        CPUSchedulingPolicy = "idle";
        IOSchedulingClass = "idle";

        Environment = [ "UNISON='${toString pairCfg.stateDirectory}'" ];
        ExecStart = ''
          ${pkgs.unison}/bin/unison \
            ${serialiseArgs pairCfg.commandOptions} \
            ${strings.concatMapStringsSep " " escapeShellArg pairCfg.roots}
        '';
      };

      Install = { WantedBy = [ "default.target" ]; };
    });
  };
}