nixos/modules/backups/git.nix
Jake Hillion 25a49899af
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
tywin: enable git backups
2023-07-03 00:04:02 +01:00

103 lines
3.0 KiB
Nix

{ config, pkgs, lib, ... }:
let
cfg = config.custom.backups.git;
in
{
options.custom.backups.git = {
enable = lib.mkEnableOption "git";
repos = lib.mkOption {
description = "A list of remotes to clone.";
type = with lib.types; listOf str;
default = [ ];
};
reposFile = lib.mkOption {
description = "A file containing the remotes to clone, one per line.";
type = with lib.types; nullOr str;
default = null;
};
sshKey = lib.mkOption {
description = "SSH private key to use when cloning repositories over SSH.";
type = with lib.types; nullOr str;
default = null;
};
};
config = lib.mkIf cfg.enable {
age.secrets."git-backups/restic/128G".file = ../../secrets/restic/128G.age;
systemd.services.backup-git = {
description = "Git repo backup service.";
serviceConfig = {
DynamicUser = true;
CacheDirectory = "backup-git";
WorkingDirectory = "%C/backup-git";
LoadCredential = [
"restic_password:${config.age.secrets."git-backups/restic/128G".path}"
] ++ (if cfg.sshKey == null then [ ] else [ "id_ecdsa:${cfg.sshKey}" ])
++ (if cfg.reposFile == null then [ ] else [ "repos_file:${cfg.reposFile}" ]);
};
environment = {
GIT_SSH_COMMAND = "${pkgs.openssh}/bin/ssh -i %d/id_ecdsa";
RESTIC_PASSWORD_FILE = "%d/restic_password";
};
script = ''
shopt -s nullglob
# Read and deduplicate repos
${if cfg.reposFile == null then "" else "readarray -t raw_repos < $CREDENTIALS_DIRECTORY/repos_file"}
declare -A repos=(${builtins.concatStringsSep " " (builtins.map (x : "[${x}]=1") cfg.repos)})
for repo in ''${raw_repos[@]}; do repos[$repo]=1; done
# Clean up existing repos
declare -A dirs
for d in *; do
origin=$(cd $d && ${pkgs.git}/bin/git remote get-url origin)
if ! [ -n "''${repos[$origin]}" ]; then
echo "$origin removed from config, cleaning up..."
rm -rf $d
else
dirs[$origin]=$d
fi
done
# Update repos
EXIT_CODE=0
for repo in "''${!repos[@]}"; do
if [ -n "''${dirs[$repo]}" ]; then
if ! (cd ''${dirs[$repo]} && ${pkgs.git}/bin/git remote update); then EXIT_CODE=1; fi
else
if ! (${pkgs.git}/bin/git clone --mirror $repo); then EXIT_CODE=1; fi
fi
done
# Backup to Restic
${pkgs.restic}/bin/restic \
-r rest:http://restic.tywin.storage.ts.hillion.co.uk/128G \
--cache-dir .restic --exclude .restic \
backup .
if test $EXIT_CODE -ne 0; then
echo "Some repositories failed to clone!"
exit $EXIT_CODE
fi
'';
};
systemd.timers.backup-git = {
wantedBy = [ "timers.target" ];
timerConfig = {
Persistent = true;
OnUnitInactiveSec = "15m";
RandomizedDelaySec = "5m";
Unit = "backup-git.service";
};
};
};
}