1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
|
#!/usr/bin/env python
import getpass
import os
import subprocess as sp
import sys
from collections import defaultdict
import pygit2
import fedmsg
import fedmsg.config
# Use $GIT_DIR to determine where this repo is.
abspath = os.path.abspath(os.environ['GIT_DIR'])
# 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 fedmsg bus."
config = fedmsg.config.load_config([], None)
config['active'] = True
config['endpoints']['relay_inbound'] = config['relay_inbound']
fedmsg.init(name='relay_inbound', cert_prefix='scm', **config)
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 = []
# 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=os.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'])
fedmsg.publish(
topic="receive",
msg=dict(commit=commit),
modname="git",
)
|