{ 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; }; 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"; }; 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"; home = "/var/sydnix/annex"; createHome = true; openssh.authorizedKeys.keyFiles = cfg.keyFiles; }; system.activationScripts.initialiseUserAnnex = let gitconfig-file = pkgs.writeText "gitconfig" '' [user] email = ${cfg.user.email} name = ${cfg.user.name} [init] defaultBranch = main [core] symlinks = true ''; in '' set -e annexHome="${config.users.users.annex.home}" ln -sf "${gitconfig-file}" "$annexHome/.gitconfig" # 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/org # Less assumptions about the host's file system! ${lib.strings.toShellVar "repos" (builtins.attrNames cfg.repos)} for repoPath in ''${!repos[@]}; do target="$annexHome/$(basename "$repoPath")" ln -sf "$repoPath" "$target" done ''; 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 # Symlink repo into user `annex` for easy access. ln -sf "$(pwd)" "$HOME/" ${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)); }; }