From 42725095248c1905d31fbc1b376afd28559dab5d Mon Sep 17 00:00:00 2001 From: Dmitriy Kholkin Date: Sun, 2 Mar 2025 16:50:12 +0300 Subject: [PATCH] feat: add persist module --- flake.lock | 16 ++++ flake.nix | 1 + modules/home/default.nix | 11 ++- modules/home/persist/default.nix | 48 ++++++++++ modules/nixos/persist/default.nix | 147 ++++++++++++++++++++++++++++++ 5 files changed, 221 insertions(+), 2 deletions(-) create mode 100644 modules/home/persist/default.nix create mode 100644 modules/nixos/persist/default.nix diff --git a/flake.lock b/flake.lock index e9b7828..e630194 100644 --- a/flake.lock +++ b/flake.lock @@ -551,6 +551,21 @@ "type": "github" } }, + "impermanence": { + "locked": { + "lastModified": 1737831083, + "narHash": "sha256-LJggUHbpyeDvNagTUrdhe/pRVp4pnS6wVKALS782gRI=", + "owner": "nix-community", + "repo": "impermanence", + "rev": "4b3e914cdf97a5b536a889e939fb2fd2b043a170", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "impermanence", + "type": "github" + } + }, "libgit2": { "flake": false, "locked": { @@ -1141,6 +1156,7 @@ "flake-parts": "flake-parts_4", "flake-registry": "flake-registry", "home-manager": "home-manager", + "impermanence": "impermanence", "lite-config": "lite-config", "lix-module": "lix-module", "nixpkgs": "nixpkgs_8", diff --git a/flake.nix b/flake.nix index 9fc1efd..dd0e53a 100644 --- a/flake.nix +++ b/flake.nix @@ -26,6 +26,7 @@ }; ataraxiasjel-nur.url = "github:AtaraxiaSjel/nur"; + impermanence.url = "github:nix-community/impermanence"; lix-module = { # url = "https://git.lix.systems/lix-project/nixos-module/archive/2.92.0.tar.gz"; url = "github:ataraxiasjel/lix-nixos-module/2.92.0-1"; diff --git a/modules/home/default.nix b/modules/home/default.nix index facb35d..c352385 100644 --- a/modules/home/default.nix +++ b/modules/home/default.nix @@ -1,4 +1,11 @@ -{ ... }: +{ lib, ... }: +let + inherit (lib) filterAttrs; + inherit (builtins) attrNames readDir; + moduleDirs = + dir: + map (name: dir + "/${name}") (attrNames (filterAttrs (_: type: type == "directory") (readDir dir))); +in { - + imports = moduleDirs ./.; } diff --git a/modules/home/persist/default.nix b/modules/home/persist/default.nix new file mode 100644 index 0000000..084f636 --- /dev/null +++ b/modules/home/persist/default.nix @@ -0,0 +1,48 @@ +{ + lib, + ... +}: +let + inherit (lib) mkOption mkEnableOption; + inherit (lib.types) listOf path str; +in +{ + options = + let + common = { + directories = mkOption { + type = listOf str; + default = [ ]; + }; + files = mkOption { + type = listOf str; + default = [ ]; + }; + }; + in + { + persist = { + enable = mkEnableOption "A tmpfs root with explicit opt-in state"; + persistRoot = mkOption { + type = path; + default = "/persist"; + }; + # Stuff that matters + # TODO backups + state = { + # backup = {...}; + } // common; + # Stuff that's just there to speed up the system + cache = { + clean = { + enable = mkEnableOption "cleaning the cache files and directories"; + dates = mkOption { + type = str; + default = "weekly"; + description = "A systemd.time calendar description of when to clean the cache files"; + }; + }; + } // common; + }; + }; +} diff --git a/modules/nixos/persist/default.nix b/modules/nixos/persist/default.nix new file mode 100644 index 0000000..70a173a --- /dev/null +++ b/modules/nixos/persist/default.nix @@ -0,0 +1,147 @@ +{ + config, + lib, + inputs, + ... +}: +let + inherit (lib) + escapeShellArg + filterAttrs + mapAttrs + mapAttrs' + mkEnableOption + mkIf + mkMerge + mkOption + nameValuePair + ; + inherit (lib.types) listOf path str; + inherit (builtins) concatMap; + cfg = config.persist; +in +{ + imports = [ inputs.impermanence.nixosModules.impermanence ]; + + options = + let + common = { + directories = mkOption { + type = listOf str; + default = [ ]; + }; + files = mkOption { + type = listOf str; + default = [ ]; + }; + }; + in + { + persist = { + enable = mkEnableOption "A tmpfs root with explicit opt-in state"; + persistRoot = mkOption { + type = path; + default = "/persist"; + }; + # Stuff that matters + # TODO backups + state = { + # backup = {...}; + } // common; + # Stuff that's just there to speed up the system + cache = { + clean = { + enable = mkEnableOption "cleaning the cache files and directories"; + dates = mkOption { + type = str; + default = "weekly"; + description = "A systemd.time calendar description of when to clean the cache files"; + }; + }; + } // common; + }; + }; + + config = + let + # TODO: fix infinite recursion (can't get user home directory) + # userPersists = lib.mapAttrs (name: cfg: + # cfg.persist // { + # home = config.users.users.${name}.home; + # } + # ) config.home-manager.users; + takeAll = what: concatMap (x: x.${what}); + persists = with cfg; [ + state + cache + ]; + allFiles = takeAll "files" persists; + allDirectories = takeAll "directories" persists; + + userPersists = mapAttrs (_: cfg: cfg.persist) config.home-manager.users; + usersFlatten = mapAttrs ( + name: cfg: + let + persists = with cfg; [ + state + cache + ]; + allHomeFiles = takeAll "files" persists; + allHomeDirectories = takeAll "directories" persists; + in + { + home = "/home/${name}"; + directories = allHomeDirectories; + files = allHomeFiles; + } + ) userPersists; + in + mkIf cfg.enable { + # Persist users uid by default + persist.state.directories = [ "/var/lib/nixos" ]; + + environment.persistence.${cfg.persistRoot} = { + hideMounts = true; + directories = allDirectories; + files = allFiles; + users = usersFlatten; + }; + + systemd.services = + let + filtered = filterAttrs (_: cfg: cfg.cache.clean.enable) userPersists; + in + mkMerge [ + (mapAttrs' ( + name: cfg: + let + absoluteHomePath = map (x: "/home/${name}/${x}"); + in + nameValuePair "persist-cache-cleanup-${name}" { + description = "Cleaning up cache files and directories for user ${name}"; + script = '' + ${builtins.concatStringsSep "\n" ( + map (x: "rm ${escapeShellArg x}") (absoluteHomePath cfg.cache.files) + )} + + ${builtins.concatStringsSep "\n" ( + map (x: "rm -rf ${escapeShellArg x}") (absoluteHomePath cfg.cache.directories) + )} + ''; + startAt = cfg.cache.clean.dates; + } + ) filtered) + { + persist-cache-cleanup = mkIf cfg.cache.clean.enable { + description = "Cleaning up cache files and directories"; + script = '' + ${builtins.concatStringsSep "\n" (map (x: "rm ${escapeShellArg x}") cfg.cache.files)} + + ${builtins.concatStringsSep "\n" (map (x: "rm -rf ${escapeShellArg x}") cfg.cache.directories)} + ''; + startAt = cfg.cache.clean.dates; + }; + } + ]; + }; +}