103 lines
3.0 KiB
Nix
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";
|
||
|
};
|
||
|
};
|
||
|
};
|
||
|
}
|