From 8e9b074467006c76768efe04cf1fb1ef9d652c67 Mon Sep 17 00:00:00 2001 From: sefidel Date: Wed, 24 Jan 2024 13:29:27 +0900 Subject: initial commit --- modules/services/gitolite/default.nix | 110 +++++++++++++++++++++++++++++++++ modules/services/gitolite/fix-refs | 9 +++ modules/services/gitolite/post-receive | 19 ++++++ modules/services/gitolite/rename | 63 +++++++++++++++++++ 4 files changed, 201 insertions(+) create mode 100644 modules/services/gitolite/default.nix create mode 100644 modules/services/gitolite/fix-refs create mode 100644 modules/services/gitolite/post-receive create mode 100644 modules/services/gitolite/rename (limited to 'modules/services/gitolite') 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] +# +# 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 -- cgit 1.4.1