diff options
-rw-r--r-- | flake/nixosConfigurations/common.nix | 33 | ||||
-rw-r--r-- | hosts/apzu/default.nix | 1 | ||||
-rw-r--r-- | hosts/tiamat/default.nix | 1 | ||||
-rw-r--r-- | modules/by-name/un/unison/module.nix | 92 | ||||
-rw-r--r-- | modules/by-name/un/unison/shellScript.nix | 95 | ||||
-rw-r--r-- | modules/home.legacy/conf/default.nix | 1 | ||||
-rw-r--r-- | modules/home.legacy/conf/unison/default.nix | 187 |
7 files changed, 221 insertions, 189 deletions
diff --git a/flake/nixosConfigurations/common.nix b/flake/nixosConfigurations/common.nix index 585e883b..2ff85521 100644 --- a/flake/nixosConfigurations/common.nix +++ b/flake/nixosConfigurations/common.nix @@ -1,6 +1,6 @@ # This file contains common configuration applied to every host. # It should only `enable` options defined in the `modules` directory. -{...}: { +{config, ...}: { soispha = { boot.enable = true; cleanup.enable = true; @@ -37,6 +37,37 @@ snapper.enable = true; steam.enable = false; systemDiff.enable = true; + unison = { + enable = true; + + foreign.userName = "soispha"; + dataDir = "${config.home-manager.users.soispha.xdg.dataHome}/unison"; + userSourceDir = "/srv/home/soispha"; + pathsToIgnore = [ + # 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" + ]; + + pathsToSync = [ + "~/.local/state/mpv" + "~/.local/state/nvim" + "~/.local/share" + "~/.local/.Trash-1000" + + "~/.mozilla/.Trash-1000" + "~/.mozilla/firefox" + + "~/media" + "~/school" + "~/repos" + ]; + }; }; programs = { diff --git a/hosts/apzu/default.nix b/hosts/apzu/default.nix index 98c83325..d1a83fec 100644 --- a/hosts/apzu/default.nix +++ b/hosts/apzu/default.nix @@ -19,6 +19,7 @@ hostName = "apzu"; mode = "NetworkManager"; }; + services.unison.foreign.address = "tiamat.fritz.box"; nixpkgs = { enable = true; systemName = "x86_64-linux"; diff --git a/hosts/tiamat/default.nix b/hosts/tiamat/default.nix index b679ec62..3e531c7d 100644 --- a/hosts/tiamat/default.nix +++ b/hosts/tiamat/default.nix @@ -19,6 +19,7 @@ backupDiskUuid = "c06ce163-2955-4388-b212-dfec4448fcf4"; enable = true; }; + unison.foreign.address = "apzu.fritz.box"; }; locale = { enable = true; diff --git a/modules/by-name/un/unison/module.nix b/modules/by-name/un/unison/module.nix new file mode 100644 index 00000000..baf92b02 --- /dev/null +++ b/modules/by-name/un/unison/module.nix @@ -0,0 +1,92 @@ +{ + lib, + config, + pkgs, + sysLib, + ... +}: let + cfg = config.soispha.services.unison; + + script = import ./shellScript.nix {inherit sysLib lib pkgs cfg;}; +in { + options.soispha.services.unison = let + homePath = lib.types.strMatching "^~.*"; + in { + enable = lib.mkEnableOption "a unison home sync script"; + + dataDir = lib.mkOption { + type = lib.types.path; + description = '' + This directory is used by unison to store it's data. + ''; + }; + + foreign = { + address = lib.mkOption { + type = lib.types.str; + description = '' + The address to contact via ssh, when syncing. + ''; + }; + userName = lib.mkOption { + type = lib.types.str; + description = '' + The user name to try to login with at the foreign host. + ''; + }; + }; + + userSourceDir = lib.mkOption { + description = '' + The directory to replace the `~` in the relative user paths with. + If using `impermanence`, this should be the path to the persistent home directory. + ''; + }; + + pathsToIgnore = lib.mkOption { + type = lib.types.listOf homePath; + default = []; + description = '' + A list of the paths that should not be synced. + Beware that this applies not only to this path, but also to all paths under it. + ''; + }; + pathsToSync = lib.mkOption { + type = lib.types.listOf homePath; + default = []; + description = '' + A list of the paths that should be synced. + Beware that this applies not only to this path, but also to all paths under it. + ''; + }; + + unisonOptions = lib.mkOption { + internal = true; + default = { + sshcmd = "ssh"; + ui = "text"; + auto = "true"; + # This is a trap, thanks to the HM links + # TODO: Auto-ignore all `home.file` paths <2024-10-24> + links = "false"; + + backupdir = "${cfg.dataDir}/backups"; + backuploc = "central"; + }; + type = lib.types.attrsOf lib.types.str; + description = "The options passed to every unison call."; + }; + }; + + config = lib.mkIf cfg.enable { + home-manager.users.soispha = { + home.sessionVariables = { + UNISON = cfg.dataDir; + }; + home.packages = [ + pkgs.unison + script + ]; + }; + }; +} diff --git a/modules/by-name/un/unison/shellScript.nix b/modules/by-name/un/unison/shellScript.nix new file mode 100644 index 00000000..5ff0c219 --- /dev/null +++ b/modules/by-name/un/unison/shellScript.nix @@ -0,0 +1,95 @@ +{ + 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}; + + 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 + ]; + } diff --git a/modules/home.legacy/conf/default.nix b/modules/home.legacy/conf/default.nix index 217944ce..161c04a1 100644 --- a/modules/home.legacy/conf/default.nix +++ b/modules/home.legacy/conf/default.nix @@ -37,7 +37,6 @@ ./taskwarrior ./timewarrior ./tridactyl - ./unison ./xdg ./yambar ./yt diff --git a/modules/home.legacy/conf/unison/default.nix b/modules/home.legacy/conf/unison/default.nix deleted file mode 100644 index ae682235..00000000 --- a/modules/home.legacy/conf/unison/default.nix +++ /dev/null @@ -1,187 +0,0 @@ -{ - 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; - }; -} |