diff options
Diffstat (limited to 'krebs')
-rw-r--r-- | krebs/4lib/default.nix | 6 | ||||
-rw-r--r-- | krebs/4lib/git.nix | 42 | ||||
-rw-r--r-- | krebs/5pkgs/git-hooks/default.nix | 168 |
3 files changed, 214 insertions, 2 deletions
diff --git a/krebs/4lib/default.nix b/krebs/4lib/default.nix index 3f27ea891..1cabeae27 100644 --- a/krebs/4lib/default.nix +++ b/krebs/4lib/default.nix @@ -3,7 +3,7 @@ with builtins; with lib; -rec { +let out = rec { eq = x: y: x == y; @@ -17,6 +17,7 @@ rec { dir.has-default-nix = path: pathExists (path + "/default.nix"); dns = import ./dns.nix { inherit lib; }; + git = import ./git.nix { lib = lib // out; }; listset = import ./listset.nix { inherit lib; }; shell = import ./shell.nix { inherit lib; }; tree = import ./tree.nix { inherit lib; }; @@ -33,4 +34,5 @@ rec { mapAttrValues = f: mapAttrs (_: f); setAttr = name: value: set: set // { ${name} = value; }; -} + +}; in out diff --git a/krebs/4lib/git.nix b/krebs/4lib/git.nix new file mode 100644 index 000000000..d50ba2018 --- /dev/null +++ b/krebs/4lib/git.nix @@ -0,0 +1,42 @@ +{ lib, ... }: + +let + inherit (lib) addNames escapeShellArg makeSearchPath optionalString; + + commands = addNames { + git-receive-pack = {}; + git-upload-pack = {}; + }; + + receive-modes = addNames { + fast-forward = {}; + non-fast-forward = {}; + create = {}; + delete = {}; + merge = {}; # TODO implement in git.nix + }; + + permissions = { + fetch = { + allow-commands = [ + commands.git-upload-pack + ]; + }; + + push = ref: extra-modes: { + allow-commands = [ + commands.git-receive-pack + commands.git-upload-pack + ]; + allow-receive-ref = ref; + allow-receive-modes = [ receive-modes.fast-forward ] ++ extra-modes; + }; + }; + + refs = { + master = "refs/heads/master"; + all-heads = "refs/heads/*"; + }; + +in +commands // receive-modes // permissions // refs diff --git a/krebs/5pkgs/git-hooks/default.nix b/krebs/5pkgs/git-hooks/default.nix new file mode 100644 index 000000000..5697c31be --- /dev/null +++ b/krebs/5pkgs/git-hooks/default.nix @@ -0,0 +1,168 @@ +{ lib, pkgs, ... }: + +with lib; + +let + out = { + inherit irc-announce; + }; + + # TODO irc-announce should return a derivation + irc-announce = { nick, channel, server, port ? 6667, verbose ? false }: '' + #! /bin/sh + set -euf + + export PATH=${makeSearchPath "bin" (with pkgs; [ + coreutils + git + gnused + ])} + + green() { printf '\x0303,99%s\x0F' "$1"; } + red() { printf '\x0304,99%s\x0F' "$1"; } + orange() { printf '\x0307,99%s\x0F' "$1"; } + pink() { printf '\x0313,99%s\x0F' "$1"; } + gray() { printf '\x0314,99%s\x0F' "$1"; } + + unset message + add_message() { + message="''${message+$message + }$*" + } + + nick=${escapeShellArg nick} + channel=${escapeShellArg channel} + server=${escapeShellArg server} + port=${toString port} + + host=$nick + cgit_endpoint=http://cgit.$host + + empty=0000000000000000000000000000000000000000 + + while read oldrev newrev ref; do + + if [ $oldrev = $empty ]; then + receive_mode=create + elif [ $newrev = $empty ]; then + receive_mode=delete + elif [ "$(git merge-base $oldrev $newrev)" = $oldrev ]; then + receive_mode=fast-forward + else + receive_mode=non-fast-forward + fi + + h=$(echo $ref | sed 's:^refs/heads/::') + + # empty_tree=$(git hash-object -t tree /dev/null) + empty_tree=4b825dc6 + + id=$(echo $newrev | cut -b-7) + id2=$(echo $oldrev | cut -b-7) + if [ $newrev = $empty ]; then id=$empty_tree; fi + if [ $oldrev = $empty ]; then id2=$empty_tree; fi + + case $receive_mode in + create) + link="$cgit_endpoint/$GIT_SSH_REPO/?h=$h" + ;; + delete) + link="$cgit_endpoint/$GIT_SSH_REPO/ ($h)" + ;; + fast-forward|non-fast-forward) + link="$cgit_endpoint/$GIT_SSH_REPO/diff/?h=$h&id=$id&id2=$id2" + ;; + esac + + #$host $GIT_SSH_REPO $ref $link + add_message $(pink push) $link $(gray "($receive_mode)") + + ${optionalString verbose '' + add_message "$( + git log \ + --format="$(orange %h) %s $(gray '(%ar)')" \ + --reverse \ + $id2..$id + + git diff --stat $id2..$id \ + | sed '$!s/\(+*\)\(-*\)$/'$(green '\1')$(red '\2')'/' + )" + ''} + + done + + if test -n "''${message-}"; then + exec ${irc-announce-script} \ + "$server" \ + "$port" \ + "$nick" \ + "$channel" \ + "$message" + fi + ''; + + irc-announce-script = pkgs.writeScript "irc-announce-script" '' + #! /bin/sh + set -euf + + export PATH=${makeSearchPath "bin" (with pkgs; [ + coreutils + gawk + gnused + netcat + nettools + ])} + + IRC_SERVER=$1 + IRC_PORT=$2 + IRC_NICK=$3$$ + IRC_CHANNEL=$4 + message=$5 + + export IRC_CHANNEL # for privmsg_cat + + # echo2 and cat2 are used output to both, stdout and stderr + # This is used to see what we send to the irc server. (debug output) + echo2() { echo "$*"; echo "$*" >&2; } + cat2() { tee /dev/stderr; } + + # privmsg_cat transforms stdin to a privmsg + privmsg_cat() { awk '{ print "PRIVMSG "ENVIRON["IRC_CHANNEL"]" :"$0 }'; } + + # ircin is used to feed the output of netcat back to the "irc client" + # so we can implement expect-like behavior with sed^_^ + # XXX mkselfdestructingtmpfifo would be nice instead of this cruft + tmpdir="$(mktemp -d irc-announce_XXXXXXXX)" + cd "$tmpdir" + mkfifo ircin + trap " + rm ircin + cd '$OLDPWD' + rmdir '$tmpdir' + trap - EXIT INT QUIT + " EXIT INT QUIT + + { + echo2 "USER $LOGNAME 0 * :$LOGNAME@$(hostname)" + echo2 "NICK $IRC_NICK" + + # wait for MODE message + sed -n '/^:[^ ]* MODE /q' + + echo2 "JOIN $IRC_CHANNEL" + + printf '%s' "$message" \ + | privmsg_cat \ + | cat2 + + echo2 "PART $IRC_CHANNEL" + + # wait for PART confirmation + sed -n '/:'"$IRC_NICK"'![^ ]* PART /q' + + echo2 'QUIT :Gone to have lunch' + } < ircin \ + | nc "$IRC_SERVER" "$IRC_PORT" | tee -a ircin + ''; + +in out |