Files
sydnix/modules/nixos/git-annex.nix
2025-02-03 20:41:05 -07:00

193 lines
6.1 KiB
Nix

{ config, lib, pkgs, ... }:
let cfg = config.sydnix.git-annex;
in {
options = {
sydnix.git-annex = {
enable = lib.mkEnableOption "git-annex";
user = {
name = lib.mkOption {
type = lib.types.str;
};
email = lib.mkOption {
type = lib.types.str;
};
};
keyFiles = lib.mkOption {
type = lib.types.listOf lib.types.path;
default = [];
};
repos = lib.mkOption {
type = lib.types.attrsOf (lib.types.submodule ({ name, ... }: {
options = {
enable = lib.mkOption {
description = ''
Git-annex repo ${name};
'';
type = lib.types.bool;
default = true;
};
symlinkToAnnexHome = lib.mkOption {
default = null;
type = lib.types.nullOr lib.types.str;
};
path = lib.mkOption {
description = ''
Path to the repo.
'';
type = lib.types.str;
default = name;
};
remotes = lib.mkOption {
type = lib.types.attrsOf lib.types.str;
default = {};
};
user = lib.mkOption {
type = lib.types.str;
default = "annex";
description = ''
The user that the Git-annex assistant will be run as.
'';
};
group = lib.mkOption {
type = lib.types.str;
default = "annex";
};
managed = lib.mkOption {
description = ''
If true, the repo will be automatically managed by Nix.
'Management' includes creating and initialising the repo and
setting permissions.
'';
type = lib.types.bool;
default = false;
};
};
}));
};
};
};
config = lib.mkIf cfg.enable {
# Git-annex assistant requires remotes to have git-annex on the PATH.
environment.systemPackages = [
pkgs.git-annex
];
# The user that users log in as to access managed repos.
users.groups.annex = {};
users.users.annex = {
# Necessary to enable cloning over SSH.
isNormalUser = true;
group = "annex";
# TODO: Don't hardcode extra groups!
extraGroups = [ "jellyfin" ];
home = "/var/sydnix/annex";
homeMode = "770";
createHome = true;
openssh.authorizedKeys.keyFiles = cfg.keyFiles;
};
# HACK: Some Jellyfin libraries are served by Git-annex. Give Jellyfin
# permission to symlink some of those libraries into the annex user's home
# dir.
users.users.${config.services.jellyfin.user}.extraGroups =
lib.mkIf config.sydnix.deertopia.jellyfin.enable
[ "annex" ];
system.activationScripts.initialiseUserAnnex =
let gitconfig-file = pkgs.writeText "gitconfig" ''
[user]
email = ${cfg.user.email}
name = ${cfg.user.name}
[init]
defaultBranch = main
[core]
symlinks = true
[safe]
${lib.strings.concatMapStrings
(repo: "\tdirectory = ${repo.path}\n")
(builtins.attrValues cfg.repos)}
'';
in ''
set -e
annexHome="${config.users.users.annex.home}"
ln -sf "${gitconfig-file}" "$annexHome/.gitconfig"
'';
systemd.services =
let
init-if-necessary = repo:
pkgs.writeScript "git-annex-init-if-necessary" ''
#!/usr/bin/env bash
set -e
[ -e .git ] || git init
[ -e .git/annex ] || git annex init
# git config set user.name "${cfg.user.name}"
# git config set user.email "${cfg.user.email}"
# Symlink repos into annex's home for easy access. This is
# particularly nice for cloning:
# git clone annex@deertopia.net:org
# instead of
# git clone annex@deertopia.net:/persist/deertopia.net/dav/org
# Less assumptions about the host's file system!
annexHome="${config.users.users.annex.home}"
${lib.optionalString (repo.symlinkToAnnexHome != null)
''ln -sf "$PWD" "$annexHome/${repo.symlinkToAnnexHome}"''}
${lib.strings.toShellVar "remotes" repo.remotes}
for remoteName in ''${!remotes[@]}; do
err=0
git remote get-url "$remoteName" || err=$?
if [ $err -eq 0 ]; then
git remote set-url "$remoteName" "''${remotes["$remoteName"]}"
else
git remote add "$remoteName" "''${remotes["$remoteName"]}"
fi
done
'';
mkAssistantService = repo: {
description = "git-annex assistant (${repo.path})";
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
serviceConfig.User = repo.user;
serviceConfig.WorkingDirectory = repo.path;
path = [ pkgs.git pkgs.git-annex pkgs.openssh ];
# Set the default file mode for new files created by this process to
# 775. Oddly, `UMask = x` will set the mode to `0777 & x`, so 002
# gets us the desired figure of 775. We want 775 because Nginx,
# running under user `nginx` needs to be able to read/write these
# files to server them over WebDAV.
serviceConfig.UMask = "002";
serviceConfig.ExecStartPre =
lib.optionalString
repo.managed
"${pkgs.bash}/bin/bash ${init-if-necessary repo}";
serviceConfig.ExecStart = ''
${pkgs.git-annex}/bin/git-annex assistant --foreground
'';
};
in builtins.listToAttrs
(builtins.map
(repo: {
name = "annex-${lib.strings.sanitizeDerivationName repo.path}";
value = mkAssistantService repo;
})
(builtins.attrValues cfg.repos));
};
}