diff --git a/roles/git/hooks/files/post-receive-chained b/roles/git/hooks/files/post-receive-chained index b7b4a3ee58..c1e973dea8 100755 --- a/roles/git/hooks/files/post-receive-chained +++ b/roles/git/hooks/files/post-receive-chained @@ -4,7 +4,7 @@ # You need to explicitly add your hook to the following list # for it to be invoked. pee \ - /usr/share/git-core/post-receive-fedmsg \ + /usr/share/git-core/post-receive-fedora-messaging \ /usr/share/git-core/post-receive-alternativearch \ /usr/lib/python2.7/site-packages/pagure/hooks/files/post-receive \ "/usr/bin/grok-manifest -m /srv/git/grokmirror/manifest.js.gz -t /srv/git/repositories/ -n `pwd`" diff --git a/roles/git/hooks/files/post-receive-fedora-messaging b/roles/git/hooks/files/post-receive-fedora-messaging new file mode 100644 index 0000000000..b8892002c1 --- /dev/null +++ b/roles/git/hooks/files/post-receive-fedora-messaging @@ -0,0 +1,172 @@ +#!/usr/bin/env python + +from __future__ import print_function, unicode_literals, absolute_import + +import getpass +import os +import subprocess as sp +import sys + +from collections import defaultdict + +import pygit2 + +import fedora_messaging.api +import fedora_messaging.exceptions + +# Use $GIT_DIR to determine where this repo is. +abspath = os.path.abspath(os.environ['GIT_DIR']) +if "/requests/" in abspath: + sys.exit(0) + +# This assumes git root dir is named "repo_name.git" +repo_name = '.'.join(abspath.split(os.path.sep)[-1].split('.')[:-1]) +namespace = abspath.split(os.path.sep)[-2] + +username = getpass.getuser() + +repo = pygit2.Repository(abspath) + +print("Emitting a message to the fedora-messaging message bus.") + + +def revs_between(head, base): + """ Yield revisions between HEAD and BASE. """ + + # pygit2 can't do a rev-list yet, so we have to shell out.. silly. + cmd = '/usr/bin/git rev-list %s...%s' % (head.id, base.id) + proc = sp.Popen(cmd.split(), stdout=sp.PIPE, stderr=sp.PIPE, cwd=abspath) + stdout, stderr = proc.communicate() + if proc.returncode != 0: + raise IOError('git rev-list failed: %r, err: %r' % (stdout, stderr)) + + for line in stdout.strip().split('\n'): + yield line.strip() + + +def build_stats(commit): + files = defaultdict(lambda: defaultdict(int)) + + # Calculate diffs against all parent commits + diffs = [repo.diff(parent, commit) for parent in commit.parents] + # Unless this is the first commit, with no parents. + diffs = diffs or [commit.tree.diff_to_tree(swap=True)] + + for diff in diffs: + for patch in diff: + if hasattr(patch, 'new_file_path'): + path = patch.new_file_path + else: + path = patch.delta.new_file.path + + if hasattr(patch, 'additions'): + files[path]['additions'] += patch.additions + files[path]['deletions'] += patch.deletions + files[path]['lines'] += patch.additions + patch.deletions + else: + files[path]['additions'] += patch.line_stats[1] + files[path]['deletions'] += patch.line_stats[2] + files[path]['lines'] += patch.line_stats[1] \ + + patch.line_stats[2] + + total = defaultdict(int) + for name, stats in files.items(): + total['additions'] += stats['additions'] + total['deletions'] += stats['deletions'] + total['lines'] += stats['lines'] + total['files'] += 1 + + return files, total + + +seen = [] + + +def getlogin(): + try: + return os.getlogin() + except: + return os.environ['USER'] + + +# Read in all the rev information git-receive-pack hands us. +lines = [line.split() for line in sys.stdin.readlines()] +for line in lines: + base, head, branch = line + branch = '/'.join(branch.split('/')[2:]) + + try: + head = repo.revparse_single(head) + except KeyError: + # This means they are deleting this branch.. and we don't have a fedmsg + # for that (yet?). It is disallowed by dist-git in Fedora anyways. + continue + + try: + base = repo.revparse_single(base) + revs = revs_between(head, base) + except KeyError: + revs = [head.id] + + def _build_commit(rev): + commit = repo.revparse_single(unicode(rev)) + + # Tags are a little funny, and vary between versions of pygit2, so we'll + # just ignore them as far as fedmsg is concerned. + if isinstance(commit, pygit2.Tag): + return None + + files, total = build_stats(commit) + + return dict( + name=commit.author.name, + email=commit.author.email, + username=username, + summary=commit.message.split('\n')[0], + message=commit.message, + stats=dict( + files=files, + total=total, + ), + rev=unicode(rev), + path=abspath, + repo=repo_name, + namespace=namespace, + branch=branch, + agent=getlogin(), + ) + + commits = map(_build_commit, revs) + + print("* Publishing information for %i commits" % len(commits)) + for commit in reversed(commits): + if commit is None: + continue + + # Keep track of whether or not we have already published this commit on + # another branch or not. It is conceivable that someone could make a + # commit to a number of branches, and push them all at the same time. + # Make a note in the fedmsg payload so we can try to reduce spam at a + # later stage. + if commit['rev'] in seen: + commit['seen'] = True + else: + commit['seen'] = False + seen.append(commit['rev']) + + try: + msg = fedora_messaging.api.Message( + topic="git.receive", + body=dict(commit=commit) + ) + fedora_messaging.api.publish(msg) + except fedora_messaging.exceptions.PublishReturned as exp: + print( + "Fedora Messaging broker rejected message %s: %s" % ( msg.id, exp) + ) + except fedora_messaging.exceptions.ConnectionException as exp: + print("Error sending message %s: %s" % (msg.id, exp)) + except Exception as exp: + print("Error sending fedora-messaging message") + print(exp) + diff --git a/roles/git/hooks/meta/main.yml b/roles/git/hooks/meta/main.yml index 2356e8716a..8b13789179 100644 --- a/roles/git/hooks/meta/main.yml +++ b/roles/git/hooks/meta/main.yml @@ -1,3 +1 @@ ---- -dependencies: -- { role: fedmsg/base } + diff --git a/roles/git/hooks/tasks/main.yml b/roles/git/hooks/tasks/main.yml index 0254e80bd0..2662a69bda 100644 --- a/roles/git/hooks/tasks/main.yml +++ b/roles/git/hooks/tasks/main.yml @@ -8,17 +8,8 @@ - git - moreutils - python-kitchen - tags: - - git - - git/hooks - - packages - -- name: install needed packages from epel testing - yum: - state: present - enablerepo: epel-testing - name: - python-pygit2 + - fedora-messaging tags: - git - git/hooks @@ -27,7 +18,7 @@ - name: install the git hooks copy: src={{item}} dest=/usr/share/git-core/ mode=0755 with_items: - - post-receive-fedmsg + - post-receive-fedora-messaging - post-receive-chained - post-receive-chained-forks - post-receive-alternativearch