diff options
Diffstat (limited to '4lib/tv')
| -rw-r--r-- | 4lib/tv/default.nix | 62 | ||||
| -rw-r--r-- | 4lib/tv/git.nix | 181 | ||||
| -rw-r--r-- | 4lib/tv/modules.nix | 21 | 
3 files changed, 264 insertions, 0 deletions
| diff --git a/4lib/tv/default.nix b/4lib/tv/default.nix new file mode 100644 index 000000000..164a6a1aa --- /dev/null +++ b/4lib/tv/default.nix @@ -0,0 +1,62 @@ +{ lib, pkgs, ... }: + +with builtins; + +let +  inherit (lib) mapAttrs stringAsChars; +in + +rec { +  git = import ./git.nix { +    lib = lib // { +      inherit addNames; +    }; +    inherit pkgs; +  }; + +  addName = name: set: +    set // { inherit name; }; + +  addNames = mapAttrs addName; + + +  # "7.4.335" -> "74" +  majmin = with lib; x : concatStrings (take 2 (splitString "." x)); + + +  concat = xs : +    if xs == [] +      then "" +      else head xs + concat (tail xs) +    ; + +  flip = f : x : y : f y x; + +  # isSuffixOf :: String -> String -> Bool +  isSuffixOf = +    s : xs : +      let +        sn = stringLength s; +        xsn = stringLength xs; +      in +        xsn >= sn && substring (xsn - sn) sn xs == s ; + +  removeSuffix = +    s : xs : substring 0 (stringLength xs - stringLength s) xs; + +  # setMap :: (String -> a -> b) -> Set String a -> [b] +  #setMap = f: xs: map (k : f k (getAttr k xs)) (attrNames xs); + +  # setToList :: Set k a -> [a] +  #setToList = setMap (_: v: v); + +  shell-escape = +    let +      isSafeChar = c: match "[-./0-9_a-zA-Z]" c != null; +    in +    stringAsChars (c: +      if isSafeChar c then c +      else if c == "\n" then "'\n'" +      else "\\${c}"); + +} diff --git a/4lib/tv/git.nix b/4lib/tv/git.nix new file mode 100644 index 000000000..8dc176114 --- /dev/null +++ b/4lib/tv/git.nix @@ -0,0 +1,181 @@ +{ lib, pkgs, ... }: + +let +  inherit (lib) addNames escapeShellArg makeSearchPath; + +  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/*"; +  }; + +  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 +  ''; + +  hooks = { +    # TODO make this a package? +    irc-announce = { nick, channel, server, port ? 6667 }: '' +      #! /bin/sh +      set -euf + +      export PATH=${makeSearchPath "bin" (with pkgs; [ +        coreutils +        git +        gnused +      ])} + +      nick=${escapeShellArg nick} +      channel=${escapeShellArg channel} +      server=${escapeShellArg server} +      port=${toString port} + +      host=$nick + +      empty=0000000000000000000000000000000000000000 + +      unset message +      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) +            #git log --oneline $id2 +            link="http://$host/cgit/$GIT_SSH_REPO/?h=$h" +            ;; +          delete) +            #git log --oneline $id2 +            link="http://$host/cgit/$GIT_SSH_REPO/ ($h)" +            ;; +          fast-forward|non-fast-forward) +            #git diff --stat $id..$id2 +            link="http://$host/cgit/$GIT_SSH_REPO/diff/?h=$h&id=$id&id2=$id2" +            ;; +        esac + +        #$host $GIT_SSH_REPO $ref $link +        message="''${message+$message +      }$GIT_SSH_USER $receive_mode $link" +      done + +      if test -n "''${message-}"; then +        exec ${irc-announce-script} \ +          "$server" \ +          "$port" \ +          "$nick" \ +          "$channel" \ +          "$message" +      fi +    ''; +  }; + +in +commands // receive-modes // permissions // refs // hooks diff --git a/4lib/tv/modules.nix b/4lib/tv/modules.nix new file mode 100644 index 000000000..248e638ea --- /dev/null +++ b/4lib/tv/modules.nix @@ -0,0 +1,21 @@ +let +  pkgs = import <nixpkgs> {}; +  inherit (pkgs.lib) concatMap hasAttr; +in rec { + +  no-touch-args = { +    config = throw "no-touch-args: can't touch config!"; +    lib = throw "no-touch-args: can't touch lib!"; +    pkgs = throw "no-touch-args: can't touch pkgs!"; +  }; + +  # list-imports : path -> [path] +  # Return a module's transitive list of imports. +  # XXX duplicates won't get eliminated from the result. +  list-imports = path: +    let module = import path no-touch-args; +        imports = if hasAttr "imports" module +                    then concatMap list-imports module.imports +                    else []; +    in [path] ++ imports; +} | 
