176 lines
4.6 KiB
Nix

{
config,
lib,
pkgs,
inputs,
...
}:
let
inherit (lib)
escapeShellArg
isAttrs
mkEnableOption
mkIf
mkOption
recursiveUpdate
;
inherit (lib.types)
either
enum
listOf
path
str
submodule
;
inherit (builtins) concatMap;
cfg = config.persist;
username = config.home.username;
homeDir = config.home.homeDirectory;
absoluteHomePath = map (x: "${homeDir}/${x}");
in
{
imports = [ inputs.impermanence.homeManagerModules.impermanence ];
options =
let
directoryEntryType = submodule {
options = {
directory = mkOption {
type = str;
description = "The directory path to be linked.";
};
method = mkOption {
type = enum [
"bindfs"
"symlink"
];
default = config.defaultDirectoryMethod;
description = ''
The linking method to be used for this specific directory entry.
'';
};
};
};
common = {
directories = mkOption {
type = listOf (either str directoryEntryType);
default = [ ];
description = ''
List of directories to persist.
Each element can be a string (e.g., ".cache") or an attribute set
(e.g., { directory = ".local/share/Steam"; method = "symlink"; }).
'';
example = [
".config/foo"
{
directory = ".config/bar";
method = "symlink";
}
];
};
files = mkOption {
type = listOf str;
default = [ ];
description = "List of files to persist.";
example = [ ".config/foo.conf" ];
};
};
in
{
persist = {
enable = mkEnableOption "A tmpfs root with explicit opt-in state";
persistRoot = mkOption {
type = path;
default = "/persist${config.home.homeDirectory}";
};
# TODO backups
state = recursiveUpdate {
# backup = {...};
} common;
cache = recursiveUpdate {
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
takeAll = what: concatMap (x: x.${what});
persists = with cfg; [
state
cache
];
allFiles = takeAll "files" persists;
allDirs = takeAll "directories" persists;
# Helper function to extract path strings from the mixed list
getPaths = map (x: if isAttrs x then x.directory else x);
in
mkIf cfg.enable {
home.persistence.${cfg.persistRoot} = {
allowOther = true;
directories = allDirs;
files = allFiles;
};
# Persist by default
persist.cache.directories = [ ".cache" ];
persist.state = {
directories = [
"Downloads"
"Documents"
"Music"
"Pictures"
"Videos"
".config/dconf"
".local/share/nix"
".local/share/systemd"
".ssh"
];
};
systemd.user = mkIf cfg.cache.clean.enable {
services."persist-cache-cleanup-${username}" = {
Unit = {
Description = "Cleaning up cache files and directories for user ${username}";
};
Service =
let
# Extract only the path strings for the cleanup script
cacheDirPaths = getPaths cfg.cache.directories;
in
{
ExecStart = pkgs.writeShellScript "" ''
${builtins.concatStringsSep "\n" (
map (x: "${pkgs.coreutils}/bin/rm ${escapeShellArg x}") (absoluteHomePath cfg.cache.files)
)}
${builtins.concatStringsSep "\n" (
map (x: "${pkgs.findutils}/bin/find ${escapeShellArg x} -mindepth 1 -delete") (
absoluteHomePath cacheDirPaths
)
)}
'';
Type = "simple";
};
};
timers."persist-cache-cleanup-${username}" = {
Unit.Description = "Run persist-cache-cleanup-${username} service by set schedule";
Timer = {
Persistent = true;
OnCalendar = cfg.cache.clean.dates;
};
Install.WantedBy = [ "timers.target" ];
};
};
};
}