{ lib, pkgs, config, ... }: let inherit (builtins) split; inherit (lib) flip foldr groupBy head literalExpression mapAttrs mapAttrs' mapAttrsToList mdDoc mkOption nameValuePair optionalAttrs types; cfg = config.services.git-http-backend; instancesByVhost = groupBy ({ value, ... }: value.vhost) (mapAttrsToList nameValuePair cfg.instances); vhostConfigs = mapAttrs (vhost: instances: foldr (l: r: l // r) {} (map ({ name, value }: let path = head (split "/+$" value.path); pathRegex = "^${path}/.*?(\.git)?/(HEAD|info/refs|git-(upload|receive)-pack)$"; in { locations = { "~ ${pathRegex}" = { proxyPass = "http://unix:/run/cgiserver/git-http-backend/${name}.sock"; extraConfig = '' client_max_body_size 0; proxy_read_timeout 3600; proxy_send_timeout 3600; ''; }; }; }) instances) ) instancesByVhost; in { options.services.git-http-backend = { package = mkOption { type = types.package; default = pkgs.gitMinimal; description = mdDoc "git package to use"; }; instances = mkOption { type = types.attrsOf (types.submodule { options = { vhost = mkOption { type = types.str; example = "spectrum-os.org"; description = mdDoc "Nginx vhost for the git server"; }; path = mkOption { type = types.strMatching "/(.*[^/])?"; default = "/"; example = "/git"; description = mdDoc '' Path to be prepended to all clone URLs. Leading slashes are mandatory; trailing slashes are forbidden. ''; }; cgiserver = mkOption { type = types.package; default = pkgs.cgiserver; defaultText = literalExpression "pkgs.cgiserver"; description = mdDoc "cgiserver package to use"; }; projectRoot = mkOption { type = types.strMatching "/(.*[^/])?"; example = "/var/www/git"; description = mdDoc '' Directory in which to look for git repositories. Leading slashes are mandatory; trailing slashes are forbidden. ''; }; }; }); default = {}; description = mdDoc "List of git-http-backend instances to run"; }; }; config = { services.nginx.virtualHosts = vhostConfigs; systemd.services = flip mapAttrs' cfg.instances (name: instance: { name = "git-http-backend-${name}"; value = { environment.GIT_HTTP_EXPORT_ALL = ""; environment.GIT_PROJECT_ROOT = instance.projectRoot; serviceConfig.DynamicUser = true; serviceConfig.ExecStart = "${instance.cgiserver}/bin/cgiserver -r ${instance.path} ${cfg.package}/bin/git-http-backend"; }; }); systemd.sockets = flip mapAttrs' cfg.instances (name: instance: { name = "git-http-backend-${name}"; value = { wantedBy = [ "sockets.target" ]; socketConfig.ListenStream = "/run/cgiserver/git-http-backend/${name}.sock"; }; }); }; }