Home

ugit @main - refs - log -
-
https://git.jolheiser.com/ugit.git
The code powering this h*ckin' site
tree log patch
multiple nix module instances
Signature
-----BEGIN SSH SIGNATURE----- U1NIU0lHAAAAAQAAADMAAAALc3NoLWVkMjU1MTkAAAAgBTEvCQk6VqUAdN2RuH6bj1dNkY oOpbPWj+jw4ua1B1cAAAADZ2l0AAAAAAAAAAZzaGE1MTIAAABTAAAAC3NzaC1lZDI1NTE5 AAAAQItLYcEkzIXqhxcUK6d6uqIV9lKib4/17JJH8NKdIGNvInsejNbuZqz5s9Wc9nfuuS yDqfc9e/i/p4y8u9+uCwk= -----END SSH SIGNATURE-----
jolheiser <git@jolheiser.com>
3 weeks ago
2 changed files, 134 additions(+), 99 deletions(-)
M cmd/ugitd/args.gocmd/ugitd/args.go
diff --git a/cmd/ugitd/args.go b/cmd/ugitd/args.go
index 24109585663c4b7156f95becaddbb4609d543d39..5dd586ad4dc96aaabe420f74e02da5e4f4b09777 100644
--- a/cmd/ugitd/args.go
+++ b/cmd/ugitd/args.go
@@ -101,6 +101,7 @@ 		return nil
 	})
 	fs.BoolVar(&c.Log.JSON, "log.json", c.Log.JSON, "Print logs in JSON(L) format")
 	fs.StringVar(&c.RepoDir, "repo-dir", c.RepoDir, "Path to directory containing repositories")
+	fs.BoolVar(&c.ShowPrivate, "show-private", c.ShowPrivate, "Show private repos in web interface")
 	fs.BoolVar(&c.SSH.Enable, "ssh.enable", c.SSH.Enable, "Enable SSH server")
 	fs.StringVar(&c.SSH.AuthorizedKeys, "ssh.authorized-keys", c.SSH.AuthorizedKeys, "Path to authorized_keys")
 	fs.StringVar(&c.SSH.CloneURL, "ssh.clone-url", c.SSH.CloneURL, "SSH clone URL base")
M nix/module.nixnix/module.nix
diff --git a/nix/module.nix b/nix/module.nix
index d544007c55b669bc4de843a580ea62fe267e4dfd..aed867c7806f164d1524db64f02382d7678de6b0 100644
--- a/nix/module.nix
+++ b/nix/module.nix
@@ -8,18 +8,13 @@ let
   cfg = config.services.ugit;
   pkg = pkgs.callPackage ./pkg.nix { inherit pkgs; };
   yamlFormat = pkgs.formats.yaml { };
-  configFile = pkgs.writeText "ugit.yaml" (
-    builtins.readFile (yamlFormat.generate "ugit-yaml" cfg.config)
-  );
-  authorizedKeysFile = pkgs.writeText "ugit_keys" (builtins.concatStringsSep "\n" cfg.authorizedKeys);
-in
-{
-  options =
+  instanceOptions =
+    { name, config, ... }:
     let
       inherit (lib) mkEnableOption mkOption types;
     in
     {
-      services.ugit = {
+      options = {
         enable = mkEnableOption "Enable ugit";
 
         package = mkOption {
@@ -28,10 +23,16 @@           description = "ugit package to use";
           default = pkg;
         };
 
+        homeDir = mkOption {
+          type = types.str;
+          description = "ugit home directory";
+          default = "/var/lib/${name}";
+        };
+
         repoDir = mkOption {
           type = types.str;
           description = "where ugit stores repositories";
-          default = "/var/lib/ugit/repos";
+          default = "/var/lib/${name}/repos";
         };
 
         authorizedKeys = mkOption {
@@ -43,13 +44,13 @@
         authorizedKeysFile = mkOption {
           type = types.str;
           description = "path to authorized_keys file ugit uses for auth";
-          default = "/var/lib/ugit/authorized_keys";
+          default = "/var/lib/${name}/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";
+          default = "/var/lib/${name}/ugit_ed25519";
         };
 
         config = mkOption {
@@ -60,21 +61,16 @@         };
 
         user = mkOption {
           type = types.str;
-          default = "ugit";
+          default = "ugit-${name}";
           description = "User account under which ugit runs";
         };
 
         group = mkOption {
           type = types.str;
-          default = "ugit";
+          default = "ugit-${name}";
           description = "Group account under which ugit runs";
         };
 
-        openFirewall = mkOption {
-          type = types.bool;
-          default = false;
-        };
-
         hooks = mkOption {
           type = types.listOf (
             types.submodule {
@@ -95,95 +91,133 @@           default = [ ];
         };
       };
     };
-  config = lib.mkIf cfg.enable {
-    users.users."${cfg.user}" = {
-      home = "/var/lib/ugit";
-      createHome = true;
-      group = "${cfg.group}";
-      isSystemUser = true;
-      isNormalUser = false;
-      description = "user for ugit service";
-    };
-    users.groups."${cfg.group}" = { };
-    networking.firewall = lib.mkIf cfg.openFirewall {
-      allowedTCPPorts = [
-        8448
-        8449
-      ];
+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);
 
-    systemd.services = {
-      ugit = {
-        enable = true;
-        script =
-          let
-            authorizedKeysPath =
-              if (builtins.length cfg.authorizedKeys) > 0 then authorizedKeysFile else cfg.authorizedKeysFile;
-            args = [
-              "--config=${configFile}"
-              "--repo-dir=${cfg.repoDir}"
-              "--ssh.authorized-keys=${authorizedKeysPath}"
-              "--ssh.host-key=${cfg.hostKeyFile}"
+    users.groups = lib.mapAttrs' (name: instanceCfg: lib.nameValuePair instanceCfg.group { }) (
+      lib.filterAttrs (name: instanceCfg: instanceCfg.enable) cfg
+    );
+
+    systemd.services = lib.foldl' (
+      acc: name:
+      let
+        instanceCfg = cfg.${name};
+      in
+      lib.recursiveUpdate acc (
+        lib.optionalAttrs instanceCfg.enable {
+          "ugit-${name}" = {
+            enable = true;
+            description = "ugit instance ${name}";
+            wantedBy = [ "multi-user.target" ];
+            after = [ "network.target" ];
+            path = [
+              instanceCfg.package
+              pkgs.git
+              pkgs.bash
             ];
-          in
-          "${cfg.package}/bin/ugitd ${builtins.concatStringsSep " " args}";
-        wantedBy = [ "multi-user.target" ];
-        after = [ "network.target" ];
-        path = [
-          cfg.package
-          pkgs.git
-          pkgs.bash
-        ];
-        serviceConfig = {
-          User = cfg.user;
-          Group = cfg.group;
-          Restart = "always";
-          RestartSec = "15";
-          WorkingDirectory = "/var/lib/ugit";
-        };
-      };
-      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 (
+            serviceConfig = {
+              User = instanceCfg.user;
+              Group = instanceCfg.group;
+              Restart = "always";
+              RestartSec = "15";
+              WorkingDirectory = instanceCfg.homeDir;
+              ExecStart =
+                let
+                  configFile = pkgs.writeText "ugit-${name}.yaml" (
+                    builtins.readFile (yamlFormat.generate "ugit-${name}-yaml" instanceCfg.config)
+                  );
+                  authorizedKeysFile = pkgs.writeText "ugit_${name}_keys" (
+                    builtins.concatStringsSep "\n" instanceCfg.authorizedKeys
+                  );
+
+                  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}";
+            };
+          };
+
+          "ugit-${name}-hooks" = {
+            description = "Setup hooks for ugit instance ${name}";
+            wantedBy = [ "multi-user.target" ];
+            after = [ "ugit-${name}.service" ];
+            requires = [ "ugit-${name}.service" ];
+            serviceConfig = {
+              Type = "oneshot";
+              RemainAfterExit = true;
+              User = instanceCfg.user;
+              Group = instanceCfg.group;
+              ExecStart =
+                let
+                  hookDir = "${instanceCfg.repoDir}/hooks/pre-receive.d";
+                  mkHookScript =
                     hook:
                     let
-                      script = pkgs.writeShellScript hook.name hook.content;
-                      path = "${cfg.repoDir}/hooks/pre-receive.d/${hook.name}";
+                      script = pkgs.writeShellScript "ugit-${name}-${hook.name}" hook.content;
                     in
-                    "ln -s ${script} ${path}"
-                  ) cfg.hooks
-                )
-              );
-            in
-            "${script}";
-        };
-      };
-    };
-
-    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}";
+                    ''
+                      mkdir -p ${hookDir}
+                      ln -sf ${script} ${hookDir}/${hook.name}
+                    '';
+                in
+                pkgs.writeShellScript "ugit-${name}-hooks-setup" ''
+                  ${builtins.concatStringsSep "\n" (map mkHookScript instanceCfg.hooks)}
+                '';
             };
           };
         }
-      ) cfg.hooks
-    );
+      )
+    ) { } (builtins.attrNames cfg);
+
+    systemd.tmpfiles.settings = lib.mapAttrs' (
+      name: instanceCfg:
+      lib.nameValuePair "ugit-${name}" (
+        builtins.listToAttrs (
+          map (
+            hook:
+            let
+              script = pkgs.writeShellScript hook.name hook.content;
+              path = "${instanceCfg.repoDir}/hooks/pre-receive.d/${hook.name}";
+            in
+            {
+              name = path;
+              value = {
+                "L" = {
+                  argument = "${script}";
+                };
+              };
+            }
+          ) instanceCfg.hooks
+        )
+      )
+    ) (lib.filterAttrs (name: instanceCfg: instanceCfg.enable) cfg);
   };
 }