about summary refs log tree commit diff
path: root/modules/server/git-http-backend
diff options
context:
space:
mode:
Diffstat (limited to 'modules/server/git-http-backend')
-rw-r--r--modules/server/git-http-backend/default.nix106
1 files changed, 106 insertions, 0 deletions
diff --git a/modules/server/git-http-backend/default.nix b/modules/server/git-http-backend/default.nix
new file mode 100644
index 000000000000..32e20e603e61
--- /dev/null
+++ b/modules/server/git-http-backend/default.nix
@@ -0,0 +1,106 @@
+{ 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";
+      };
+    });
+  };
+}