about summary refs log tree commit diff stats
path: root/modules/home.legacy/conf/unison/default.nix
diff options
context:
space:
mode:
Diffstat (limited to 'modules/home.legacy/conf/unison/default.nix')
-rw-r--r--modules/home.legacy/conf/unison/default.nix187
1 files changed, 187 insertions, 0 deletions
diff --git a/modules/home.legacy/conf/unison/default.nix b/modules/home.legacy/conf/unison/default.nix
new file mode 100644
index 00000000..ae682235
--- /dev/null
+++ b/modules/home.legacy/conf/unison/default.nix
@@ -0,0 +1,187 @@
+{
+  lib,
+  config,
+  nixosConfig,
+  sysLib,
+  pkgs,
+  ...
+}: let
+  unisonPath = "${config.xdg.dataHome}/unison";
+
+  # These are only used for the script
+  unisonOptions = {
+    sshcmd = "ssh";
+    ui = "text";
+    auto = "true";
+    # This is useless, with hm links
+    links = "false";
+
+    backupdir = "${unisonPath}/backups";
+    backuploc = "central";
+    backupcurr = paths_to_merge;
+    # merge =
+    #   builtins.map (x: ''${x} -> diff3 --text --merge CURRENT1 CURRENTARCH CURRENT2 > NEW'')
+    #   paths_to_merge;
+  };
+
+  paths_to_merge = mkPathName {
+    file_names = ["log" "history" "harpoon.json" "file_frecency.bin" "main.shada"];
+    extensions = ["log"];
+  };
+
+  paths_to_keep = [
+    "~/.local/state/mpv"
+    "~/.local/state/nvim"
+    "~/.local/share"
+    "~/.local/.Trash-1000"
+
+    "~/.mozilla/.Trash-1000"
+    "~/.mozilla/firefox"
+
+    "~/media"
+    "~/school"
+    "~/repos"
+  ];
+  paths_to_ignore = [
+    # already synchronized by the taskserver
+    "~/.local/share/task"
+
+    # Should not be synchronized
+    "~/.local/share/unison"
+
+    # Is just to big to be synchronized (# TODO: Work around that <2024-08-31> )
+    "~/media/music"
+  ];
+
+  hostName = let
+    hn = nixosConfig.networking.hostName;
+  in
+    if hn == "tiamat"
+    then "apzu"
+    else if hn == "apzu"
+    then "tiamat"
+    else builtins.throw "Host (${hn}) not yet covered in the unison host mapping.";
+
+  mkPathName = {
+    file_names,
+    extensions,
+  }:
+    builtins.map (x: ''Name ${x}'') (
+      (builtins.map (x: ''*.${x}'') extensions)
+      ++ file_names
+    );
+
+  unitName = name: builtins.replaceStrings ["/"] ["-"] name;
+
+  mkPath = path:
+    if lib.strings.hasPrefix "~" path
+    then "${builtins.elemAt (builtins.attrNames config.home.persistence)
+      0}${lib.strings.removePrefix "~" path}"
+    else
+      builtins.throw
+      "Every pathname needs to start with a '~'";
+
+  mkPair = pathname: let
+    path = mkPath pathname;
+  in {
+    name = unitName "${pathname}";
+    value = {
+      stateDirectory = unisonPath;
+      roots = [
+        "${path}"
+        "ssh://${config.home.username}@${hostName}.fritz.box/${path}"
+      ];
+    };
+  };
+
+  getIgnoredSingle = path: path_to_ignore: let
+    clean_path_to_ignore = mkPath path_to_ignore;
+    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;
+
+  getIgnored = paths_to_ignore: path:
+    serialiseArgs {
+      ignore =
+        builtins.filter (x: x != null) (builtins.map (getIgnoredSingle path) paths_to_ignore);
+    };
+
+  serialiseArg = key: val:
+    if builtins.typeOf val == "string"
+    then lib.strings.escapeShellArg "-${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
+    );
+
+  esa = a: lib.strings.escapeShellArg a;
+
+  mkScriptLine = pathname: let
+    path =
+      mkPath pathname;
+  in
+    lib.strings.concatStringsSep " " [
+      "unison"
+      "${serialiseArgs unisonOptions}"
+      "$EXTRA_OPTIONS"
+      "${getIgnored paths_to_ignore path}"
+      "${esa path}"
+      (esa "ssh://${config.home.username}@${hostName}.fritz.box/${path}")
+    ];
+
+  script = lib.strings.concatStringsSep "\n" (builtins.map mkScriptLine paths_to_keep);
+
+  pairs = builtins.listToAttrs (builtins.map mkPair paths_to_keep);
+in {
+  home.sessionVariables = {
+    UNISON = unisonPath;
+  };
+  home.packages = [
+    pkgs.unison
+    (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 unisonPath};
+
+          if [ "$1" = "links" ]; then
+            shift 1;
+            EXTRA_OPTIONS="-links=true";
+          fi
+          EXTRA_OPTIONS="$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
+      ];
+    })
+  ];
+  services.unison = {
+    enable = false;
+    inherit pairs;
+  };
+}