{ 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)); }; }