borgmatic: optionally exclude HM symlinks from backup

Co-authored-by: Naïm Favier <n@monade.li>
Co-authored-by: Robert Helgesson <robert@rycee.net>
This commit is contained in:
Damien Cassou 2023-03-19 11:28:02 +01:00 committed by Robert Helgesson
parent 2ddd4e151d
commit a8f5ca239f
No known key found for this signature in database
GPG key ID: 36BDAA14C2797E89
5 changed files with 171 additions and 3 deletions

View file

@ -43,7 +43,10 @@ let
};
};
configModule = types.submodule {
configModule = types.submodule ({ config, ... }: {
config.location.extraConfig.exclude_from =
mkIf config.location.excludeHomeManagerSymlinks
(mkAfter [ (toString hmExcludeFile) ]);
options = {
location = {
sourceDirectories = mkOption {
@ -59,6 +62,18 @@ let
literalExpression ''["ssh://myuser@myrepo.myserver.com/./repo"]'';
};
excludeHomeManagerSymlinks = mkOption {
type = types.bool;
description = ''
Whether to exclude Home Manager generated symbolic links from
the backups. This facilitates restoring the whole home
directory when the Nix store doesn't contain the latest
Home Manager generation.
'';
default = false;
example = true;
};
extraConfig = extraConfigOption;
};
@ -120,10 +135,18 @@ let
extraConfig = extraConfigOption;
};
};
};
});
removeNullValues = attrSet: filterAttrs (key: value: value != null) attrSet;
hmFiles = builtins.attrValues config.home.file;
hmSymlinks = (lib.filter (file: !file.recursive) hmFiles);
hmExcludePattern = file: ''
${config.home.homeDirectory}/${file.target}
'';
hmExcludePatterns = lib.concatMapStrings hmExcludePattern hmSymlinks;
hmExcludeFile = pkgs.writeText "hm-symlinks.txt" hmExcludePatterns;
writeConfig = config:
generators.toYAML { } {
location = removeNullValues {

View file

@ -1 +1,7 @@
{ borgmatic-program-basic-configuration = ./basic-configuration.nix; }
{
borgmatic-program-basic-configuration = ./basic-configuration.nix;
borgmatic-program-include-hm-symlinks = ./include-hm-symlinks.nix;
borgmatic-program-exclude-hm-symlinks = ./exclude-hm-symlinks.nix;
borgmatic-program-exclude-hm-symlinks-nothing-else =
./exclude-hm-symlinks-nothing-else.nix;
}

View file

@ -0,0 +1,40 @@
{ config, pkgs, ... }:
let
backups = config.programs.borgmatic.backups;
excludeFile = pkgs.writeText "excludeFile.txt" "/foo/bar";
in {
config = {
programs.borgmatic = {
enable = true;
backups = {
main = {
location = {
sourceDirectories = [ "/my-stuff-to-backup" ];
repositories = [ "/mnt/disk1" ];
excludeHomeManagerSymlinks = true;
};
};
};
};
test.stubs.borgmatic = { };
nmt.script = ''
config_file=$TESTED/home-files/.config/borgmatic.d/main.yaml
assertFileExists $config_file
yq=${pkgs.yq-go}/bin/yq
hmExclusionsFile=$($yq '.location.exclude_from[0]' $config_file)
expected_content='/home/hm-user/.config/borgmatic.d/main.yaml'
grep --quiet "$expected_content" "$hmExclusionsFile"
if [[ $? -ne 0 ]]; then
echo "Expected to find $expected_content in file $hmExclusionsFile but didn't" >&2
exit 1
fi
'';
};
}

View file

@ -0,0 +1,54 @@
{ config, pkgs, ... }:
let
backups = config.programs.borgmatic.backups;
excludeFile = pkgs.writeText "excludeFile.txt" "/foo/bar";
in {
config = {
programs.borgmatic = {
enable = true;
backups = {
main = {
location = {
sourceDirectories = [ "/my-stuff-to-backup" ];
repositories = [ "/mnt/disk1" ];
excludeHomeManagerSymlinks = true;
extraConfig = { exclude_from = [ (toString excludeFile) ]; };
};
};
};
};
test.stubs.borgmatic = { };
nmt.script = ''
config_file=$TESTED/home-files/.config/borgmatic.d/main.yaml
assertFileExists $config_file
declare -A expectations
expectations[location.exclude_from[0]]="${excludeFile}"
yq=${pkgs.yq-go}/bin/yq
for filter in "''${!expectations[@]}"; do
expected_value="''${expectations[$filter]}"
actual_value="$($yq ".$filter" $config_file)"
if [[ "$actual_value" != "$expected_value" ]]; then
fail "Expected '$filter' to be '$expected_value' but was '$actual_value'"
fi
done
hmExclusionsFile=$($yq '.location.exclude_from[1]' $config_file)
expected_content='/home/hm-user/.config/borgmatic.d/main.yaml'
grep --quiet "$expected_content" "$hmExclusionsFile"
if [[ $? -ne 0 ]]; then
echo "Expected to find $expected_content in file $hmExclusionsFile but didn't" >&2
exit 1
fi
'';
};
}

View file

@ -0,0 +1,45 @@
{ config, pkgs, ... }:
let
backups = config.programs.borgmatic.backups;
excludeFile = pkgs.writeText "excludeFile.txt" "/foo/bar";
in {
config = {
programs.borgmatic = {
enable = true;
backups = {
main = {
location = {
sourceDirectories = [ "/my-stuff-to-backup" ];
repositories = [ "/mnt/disk1" ];
excludeHomeManagerSymlinks = false;
extraConfig = { exclude_from = [ (toString excludeFile) ]; };
};
};
};
};
test.stubs.borgmatic = { };
nmt.script = ''
config_file=$TESTED/home-files/.config/borgmatic.d/main.yaml
assertFileExists $config_file
declare -A expectations
expectations[location.exclude_from[0]]="${excludeFile}"
yq=${pkgs.yq-go}/bin/yq
for filter in "''${!expectations[@]}"; do
expected_value="''${expectations[$filter]}"
actual_value="$($yq ".$filter" $config_file)"
if [[ "$actual_value" != "$expected_value" ]]; then
fail "Expected '$filter' to be '$expected_value' but was '$actual_value'"
fi
done
'';
};
}