{ pkgs, lib, config, ... }: let cfg = config.services.ugit; pkg = pkgs.callPackage ./pkg.nix { inherit pkgs; }; yamlFormat = pkgs.formats.yaml { }; instanceOptions = let inherit (lib) mkEnableOption mkOption types; in { options = { enable = mkEnableOption "Enable ugit"; package = mkOption { type = types.package; description = "ugit package to use"; default = pkg; }; homeDir = mkOption { type = types.str; description = "ugit home directory"; default = "/var/lib/ugit"; }; repoDir = mkOption { type = types.str; description = "where ugit stores repositories"; default = "/var/lib/ugit/repos"; }; authorizedKeys = mkOption { type = types.listOf types.str; description = "list of keys to use for authorized_keys"; default = [ ]; }; authorizedKeysFile = mkOption { type = types.str; description = "path to authorized_keys file ugit uses for auth"; default = "/var/lib/ugit/authorized_keys"; }; hostKeyFile = mkOption { type = types.str; description = "path to host key file (will be created if it doesn't exist)"; default = "/var/lib/ugit/ugit_ed25519"; }; config = mkOption { type = types.attrs; default = { }; description = "config.yaml contents"; }; user = mkOption { type = types.str; default = "ugit"; description = "User account under which ugit runs"; }; group = mkOption { type = types.str; default = "ugit"; description = "Group account under which ugit runs"; }; hooks = mkOption { type = types.listOf ( types.submodule { options = { name = mkOption { type = types.str; description = "Hook name"; }; content = mkOption { type = types.str; description = "Hook contents"; }; }; } ); description = "A list of pre-receive hooks to run"; default = [ ]; }; }; }; in { options = { services.ugit = lib.mkOption { type = lib.types.attrsOf (lib.types.submodule instanceOptions); default = { }; description = "Attribute set of ugit instances"; }; }; config = lib.mkIf (cfg != { }) { users.users = lib.mapAttrs' ( name: instanceCfg: lib.nameValuePair instanceCfg.user { home = instanceCfg.homeDir; createHome = true; group = instanceCfg.group; isSystemUser = true; isNormalUser = false; description = "user for ugit ${name} service"; } ) (lib.filterAttrs (name: instanceCfg: instanceCfg.enable) cfg); users.groups = lib.mapAttrs' (name: instanceCfg: lib.nameValuePair instanceCfg.group { }) ( lib.filterAttrs (name: instanceCfg: instanceCfg.enable) cfg ); systemd.services = lib.mapAttrs' ( name: instanceCfg: lib.nameValuePair "ugit-${name}" { ugit = let configFile = pkgs.writeText "ugit.yaml" ( builtins.readFile (yamlFormat.generate "ugit-yaml" instanceCfg.config) ); authorizedKeysFile = pkgs.writeText "ugit_keys" ( builtins.concatStringsSep "\n" instanceCfg.authorizedKeys ); in { enable = true; script = let authorizedKeysPath = if (builtins.length instanceCfg.authorizedKeys) > 0 then authorizedKeysFile else instanceCfg.authorizedKeysFile; args = [ "--config=${configFile}" "--repo-dir=${instanceCfg.repoDir}" "--ssh.authorized-keys=${authorizedKeysPath}" "--ssh.host-key=${instanceCfg.hostKeyFile}" ]; in "${instanceCfg.package}/bin/ugitd ${builtins.concatStringsSep " " args}"; wantedBy = [ "multi-user.target" ]; after = [ "network.target" ]; path = [ instanceCfg.package pkgs.git pkgs.bash ]; serviceConfig = { User = instanceCfg.user; Group = instanceCfg.group; Restart = "always"; RestartSec = "15"; WorkingDirectory = instanceCfg.homeDir; }; }; ugit-hooks = { wantedBy = [ "multi-user.target" ]; after = [ "ugit.service" ]; requires = [ "ugit.service" ]; serviceConfig = { Type = "oneshot"; ExecStart = let script = pkgs.writeShellScript "ugit-hooks-link" ( builtins.concatStringsSep "\n" ( map ( hook: let script = pkgs.writeShellScript hook.name hook.content; path = "${instanceCfg.repoDir}/hooks/pre-receive.d/${hook.name}"; in "ln -s ${script} ${path}" ) instanceCfg.hooks ) ); in "${script}"; }; }; } ) (lib.filterAttrs (name: instanceCfg: instanceCfg.enable) cfg); systemd.tmpfiles.settings.ugit = builtins.listToAttrs ( map ( hook: let script = pkgs.writeShellScript hook.name hook.content; path = "${cfg.repoDir}/hooks/pre-receive.d/${hook.name}"; in { name = path; value = { "L" = { argument = "${script}"; }; }; } ) cfg.hooks ); }; }