about summary refs log tree commit diff
path: root/modules/services/gitolite
diff options
context:
space:
mode:
authorsefidel <contact@sefidel.net>2024-01-24 13:29:27 +0900
committersefidel <contact@sefidel.net>2024-01-24 18:59:54 +0900
commit8e9b074467006c76768efe04cf1fb1ef9d652c67 (patch)
treefad73c7f94a74c77714c260d1fc0a63e2d205b49 /modules/services/gitolite
downloadinfra-modules-main.tar.gz
infra-modules-main.zip
initial commit HEAD main
Diffstat (limited to 'modules/services/gitolite')
-rw-r--r--modules/services/gitolite/default.nix110
-rw-r--r--modules/services/gitolite/fix-refs9
-rw-r--r--modules/services/gitolite/post-receive19
-rw-r--r--modules/services/gitolite/rename63
4 files changed, 201 insertions, 0 deletions
diff --git a/modules/services/gitolite/default.nix b/modules/services/gitolite/default.nix
new file mode 100644
index 0000000..31cf755
--- /dev/null
+++ b/modules/services/gitolite/default.nix
@@ -0,0 +1,110 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+let
+  cfg = config.modules.services.gitolite;
+in
+{
+  options.modules.services.gitolite = {
+    enable = mkEnableOption "gitolite server";
+    adminPubkey = mkOption { type = types.str; };
+  };
+  config = mkIf cfg.enable {
+    services.openssh.enable = true;
+
+    services.gitolite = {
+      enable = true;
+      user = "git";
+      group = "git";
+      adminPubkey = cfg.adminPubkey;
+      extraGitoliteRc = ''
+        $RC{UMASK} = 0027;
+        $RC{GIT_CONFIG_KEYS} = '.*';
+        $RC{ROLES}{OWNERS} = 1;
+        $RC{OWNER_ROLENAME} = 'OWNERS';
+        # For some unknown reasons, $ENV{HOME} doesn't get resolved to the correct
+        # directory.
+        # $RC{LOCAL_CODE} = '$ENV{HOME}/local';
+        $RC{LOCAL_CODE} = '/var/lib/gitolite/local';
+        push(@{$RC{ENABLE}}, 'D');
+        push(@{$RC{ENABLE}}, 'symbolic-ref');
+        push(@{$RC{ENABLE}}, 'rename');
+        push(@{$RC{POST_GIT}}, 'fix-refs');
+        # push(@{$RC{ENABLE}}, 'set-default-roles');
+        # push(@{$RC{ENABLE}}, 'create');
+        # push(@{$RC{ENABLE}}, 'fork');
+
+      '';
+    };
+
+    modules.persistence.directories = [
+      "/var/lib/gitolite"
+    ];
+
+    system.activationScripts.gitolite-create-local = ''
+      mkdir -p /var/lib/gitolite/local/triggers
+      mkdir -p /var/lib/gitolite/local/commands
+      mkdir -p /var/lib/gitolite/local/hooks/common
+      chown -R git:git /var/lib/gitolite/local
+    '';
+
+    systemd.tmpfiles.rules = [
+      # https://groups.google.com/g/gitolite/c/NwZ1-hq9-9E/m/mDbiKyAvDwAJ
+      "C /var/lib/gitolite/local/triggers/fix-refs 755 - - - ${./fix-refs}"
+      "C /var/lib/gitolite/local/commands/rename 755 - - - ${./rename}"
+      "C /var/lib/gitolite/local/hooks/common/post-receive 755 - - - ${./post-receive}"
+    ];
+
+
+    systemd.timers."gitolite-trash-cleanup" = {
+      wantedBy = [ "timers.target" ];
+      timerConfig = {
+        OnCalendar = "*-*-* 00:00:00";
+        Unit = "gitolite-trash-cleanup.service";
+      };
+    };
+
+    systemd.services."gitolite-trash-cleanup" = {
+      script = ''
+        set -euo pipefail
+        if [ ! -d "Trash" ] ; then
+          echo Trash directory is nonexistent!
+          echo No operations to perform. Exiting.
+          exit 0
+        fi
+
+        match=$(find Trash -type d -regextype posix-extended -regex ".*/[0-9]{4}-[0-9]{2}-[0-9]{2}_[0-9]{2}:[0-9]{2}:[0-9]{2}$")
+        processed_entry=0
+        removed_entry=0
+
+        for dir in $match
+        do
+          system_timestamp=$(date +%s)
+          trash_timestamp=$(basename $dir | sed -e "s/_/ /g" | date -f - +%s)
+          age=$(( $system_timestamp - $trash_timestamp ))
+          # Wipe trashes older than 2w
+          if [[ age -gt 1209600 ]] ; then
+            echo "Removing '$dir' (age $age)"
+            rm -rf $dir
+            ((removed_entry+=1))
+          fi
+          ((processed_entry+=1))
+        done
+
+        echo "Directories that needs cleanup:"
+        find Trash -type d -empty -print -delete
+        echo "Cleaned empty directories."
+
+        echo "Done! Removed $removed_entry/$processed_entry"
+      '';
+
+      path = with pkgs; [ bash util-linux coreutils ];
+
+      serviceConfig = {
+        Type = "oneshot";
+        User = "git";
+        WorkingDirectory = "/var/lib/gitolite/repositories";
+      };
+    };
+  };
+}
diff --git a/modules/services/gitolite/fix-refs b/modules/services/gitolite/fix-refs
new file mode 100644
index 0000000..8ffec9e
--- /dev/null
+++ b/modules/services/gitolite/fix-refs
@@ -0,0 +1,9 @@
+[[ $4 == W ]] || exit 0
+
+cd $GL_REPO_BASE/$2.git
+
+head=`git symbolic-ref HEAD`
+[[ -f $head ]] || {
+  set -- refs/heads/*
+  git symbolic-ref HEAD $1
+}
diff --git a/modules/services/gitolite/post-receive b/modules/services/gitolite/post-receive
new file mode 100644
index 0000000..2f72ae9
--- /dev/null
+++ b/modules/services/gitolite/post-receive
@@ -0,0 +1,19 @@
+#!/bin/sh
+#
+# An example hook to update the "agefile" for CGit's idle time calculation.
+#
+# This hook assumes that you are using the default agefile location of
+# "info/web/last-modified".  If you change the value in your cgitrc then you
+# must also change it here.
+#
+# To install the hook, copy (or link) it to the file "hooks/post-receive" in
+# each of your repositories.
+#
+
+agefile="$(git rev-parse --git-dir)"/info/web/last-modified
+
+mkdir -p "$(dirname "$agefile")" &&
+git for-each-ref \
+	--sort=-authordate --count=1 \
+	--format='%(authordate:iso8601)' \
+	>"$agefile"
diff --git a/modules/services/gitolite/rename b/modules/services/gitolite/rename
new file mode 100644
index 0000000..2b00c7a
--- /dev/null
+++ b/modules/services/gitolite/rename
@@ -0,0 +1,63 @@
+
+# Usage:    ssh git@host rename [-c] <repo1> <repo2>
+#
+# Renames repo1 to repo2. You must be the creator of repo1, and have
+# create ("C") permissions for repo2, which of course must not exist.
+# Alternatively you must be an account admin, that is, you must have
+# write access to the gitolite-admin repository. If you have "C"
+# permissions for repo2 then you can use the -c option to take over
+# as creator of the repository.
+
+die() { echo "$@" >&2; exit 1; }
+usage() { perl -lne 'print substr($_, 2) if /^# Usage/../^$/' < $0; exit 1; }
+[ -z "$1" ] && usage
+[ "$1" = "-h" ] && usage
+[ -z "$GL_USER" ] && die GL_USER not set
+
+# ----------------------------------------------------------------------
+
+if [ "$1" = "-c" ]
+then	shift
+	takeover=true
+else	takeover=false
+fi
+
+from="$1"; shift
+to="$1"; shift
+[ -z "$to" ] && usage
+
+topath=$GL_REPO_BASE/$to.git
+
+checkto() {
+	gitolite access -q "$to" $GL_USER ^C any ||
+		die "'$to' already exists or you are not allowed to create it"
+}
+
+if gitolite access -q gitolite-admin $GL_USER
+then
+	# the user is an admin so we can avoid most permission checks
+	if $takeover
+	then checkto
+	elif [ -e $topath ]
+	then die "'$to' already exists"
+	fi
+else
+	# the user isn't an admin, so do all the checks
+	checkto
+	gitolite creator "$from" $GL_USER ||
+		die "'$from' does not exist or you are not allowed to delete it"
+fi
+
+# ----------------------------------------------------------------------
+
+mv $GL_REPO_BASE/$from.git $topath
+[ $? -ne 0 ] && exit 1
+
+$takeover && echo $GL_USER > $topath/gl-creator
+
+# Rebuild projects.list
+gitolite trigger POST_COMPILE
+
+echo "$from renamed to $to" >&2
+
+exit