about summary refs log tree commit diff stats
path: root/modules/by-name/un/unison/shellScript.nix
blob: 4618ae8e47aa26ec579aa57a62c085e75d4afe38 (plain) (blame)
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
{
  sysLib,
  lib,
  pkgs,
  cfg,
}: let
  esa = lib.strings.escapeShellArg;

  expandHomePath = path:
    if lib.strings.hasPrefix "~" path
    then "${cfg.userSourceDir}${lib.strings.removePrefix "~" path}"
    else
      builtins.throw
      ''
        BUG: Every pathname needs to start with a '~'.
        This should have been checked by the NixOS module system?
      '';

  getIgnored = paths_to_ignore: path:
    serialiseArgs {
      ignore =
        builtins.filter (x: x != null) (builtins.map (getIgnoredSingle path) paths_to_ignore);
    };

  getIgnoredSingle = path: pathToIgnore: let
    clean_path_to_ignore = expandHomePath pathToIgnore;
    commonPath = builtins.substring 0 (builtins.stringLength path) clean_path_to_ignore;
  in
    if commonPath == path
    then let
      preFinalPath =
        builtins.substring (builtins.stringLength commonPath)
        (builtins.stringLength clean_path_to_ignore)
        clean_path_to_ignore;
      finalPath =
        if lib.strings.hasPrefix "/" preFinalPath
        then lib.strings.removePrefix "/" preFinalPath
        else preFinalPath;
    in "BelowPath ${finalPath}"
    else null;

  serialiseArg = key: val:
    if builtins.typeOf val == "string"
    then esa "-${key}=${lib.strings.escape ["="] val}"
    else if builtins.typeOf val == "list"
    then lib.strings.concatStringsSep " " (builtins.map (serialiseArg key) val)
    else builtins.throw "Unsupported type: ${builtins.typeOf val}";

  serialiseArgs = args:
    lib.strings.concatStringsSep " " (
      lib.attrsets.mapAttrsToList
      serialiseArg
      args
    );

  mkScriptLine = pathname: let
    path =
      expandHomePath pathname;
  in
    lib.strings.concatStringsSep " " [
      "unison"
      "${serialiseArgs cfg.unisonOptions}"
      "$EXTRA_OPTIONS"
      "${getIgnored cfg.pathsToIgnore path}"
      "${esa path}"
      (esa "ssh://${cfg.foreign.userName}@${cfg.foreign.address}/${path}")
    ];

  script = lib.strings.concatStringsSep "\n" (builtins.map mkScriptLine cfg.pathsToSync);
in
  sysLib.writeShellScript {
    name = "unison-sync";
    src = builtins.toFile "unison-backup" (''
        #!/usr/bin/env dash

        # shellcheck source=/dev/null
        SHELL_LIBRARY_VERSION="2.1.2" . %SHELL_LIBRARY_PATH

        export UNISON=${esa cfg.dataDir};

        EXTRA_OPTIONS="$UNISON_EXTRA_OPTIONS $*"
      ''
      + script);

    dependencies = with pkgs; [
      unison
      openssh # needed to connect to the other server
      less # needed to show diffs
      diffutils # needed to compute diffs
    ];
  }