forked from atseanpaul/review-o-matic
-
Notifications
You must be signed in to change notification settings - Fork 0
/
troll-o-matic.py
executable file
·221 lines (187 loc) · 7.77 KB
/
troll-o-matic.py
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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
#!/usr/bin/env python3
from reviewer import Reviewer
from gerrit import Gerrit, GerritRevision, GerritMessage
from trollreview import ReviewType
from trollreviewerfromgit import FromgitChangeReviewer
from trollreviewerupstream import UpstreamChangeReviewer
from trollreviewerfromlist import FromlistChangeReviewer
from trollreviewerchromium import ChromiumChangeReviewer
import argparse
import datetime
import json
import requests
import sys
import time
class Troll(object):
def __init__(self, url, args):
self.url = url
self.args = args
self.gerrit = Gerrit(url)
self.tag = 'autogenerated:review-o-matic'
self.blacklist = {}
self.stats = { str(ReviewType.SUCCESS): 0, str(ReviewType.BACKPORT): 0,
str(ReviewType.ALTERED_UPSTREAM): 0,
str(ReviewType.MISSING_FIELDS): 0,
str(ReviewType.MISSING_HASH): 0,
str(ReviewType.INVALID_HASH): 0,
str(ReviewType.MISSING_AM): 0,
str(ReviewType.INCORRECT_PREFIX): 0,
str(ReviewType.FIXES_REF): 0,
str(ReviewType.KCONFIG_CHANGE): 0,
str(ReviewType.IN_MAINLINE): 0,
str(ReviewType.UPSTREAM_COMMENTS): 0 }
def inc_stat(self, review_type):
if self.args.dry_run:
return
key = str(review_type)
if not self.stats.get(key):
self.stats[key] = 1
else:
self.stats[key] += 1
def do_review(self, change, review):
print('Review for change: {}'.format(change.url()))
print(' Issues: {}, Feedback: {}, Vote:{}, Notify:{}'.format(
review.issues.keys(), review.feedback.keys(), review.vote,
review.notify))
if review.dry_run:
print(review.generate_review_message())
if review.inline_comments:
print('')
print('-- Inline comments:')
for f,comments in review.inline_comments.items():
for c in comments:
print('{}:{}'.format(f, c['line']))
print(c['message'])
print('------')
return
for i in review.issues:
self.inc_stat(i)
for f in review.feedback:
self.inc_stat(f)
self.gerrit.review(change, self.tag, review.generate_review_message(),
review.notify, vote_code_review=review.vote,
inline_comments=review.inline_comments)
def get_changes(self, prefix):
message = '{}:'.format(prefix)
after = datetime.date.today() - datetime.timedelta(days=5)
changes = self.gerrit.query_changes(status='open', message=message,
after=after, project='chromiumos/third_party/kernel')
return changes
def add_change_to_blacklist(self, change):
self.blacklist[change.number] = change.current_revision.number
def is_change_in_blacklist(self, change):
return self.blacklist.get(change.number) == change.current_revision.number
def process_changes(self, changes):
rev = Reviewer(git_dir=self.args.git_dir, verbose=self.args.verbose,
chatty=self.args.chatty)
ret = 0
for c in changes:
if self.args.verbose:
print('Processing change {}'.format(c.url()))
force_review = self.args.force_cl or self.args.force_all
days_since_last_review = None
if not force_review:
for m in c.messages:
if m.tag == self.tag and m.revision_num == c.current_revision.number:
days_since_last_review = (datetime.datetime.utcnow() - m.date).days
if self.args.verbose and days_since_last_review != None:
print(' Reviewed {} days ago'.format(days_since_last_review))
# Find a reviewer and blacklist if not found
reviewer = None
if FromlistChangeReviewer.can_review_change(c, days_since_last_review):
reviewer = FromlistChangeReviewer(rev, c, self.args.dry_run)
elif FromgitChangeReviewer.can_review_change(c, days_since_last_review):
reviewer = FromgitChangeReviewer(rev, c, self.args.dry_run,
days_since_last_review)
elif UpstreamChangeReviewer.can_review_change(c, days_since_last_review):
reviewer = UpstreamChangeReviewer(rev, c, self.args.dry_run)
elif self.args.kconfig_hound and \
ChromiumChangeReviewer.can_review_change(c, days_since_last_review):
reviewer = ChromiumChangeReviewer(rev, c, self.args.dry_run,
self.args.verbose)
if not reviewer:
self.add_change_to_blacklist(c)
continue
if not force_review and self.is_change_in_blacklist(c):
continue
result = reviewer.review_patch()
if result:
self.do_review(c, result)
ret += 1
self.add_change_to_blacklist(c)
return ret
def update_stats(self):
if not self.args.dry_run and self.args.stats_file:
with open(self.args.stats_file, 'wt') as f:
json.dump(self.stats, f)
print('--')
summary = ' Summary: '
total = 0
for k,v in self.stats.items():
summary += '{}={} '.format(k,v)
total += v
summary += 'total={}'.format(total)
print(summary)
print('')
def run(self):
if self.args.force_cl:
c = self.gerrit.get_change(self.args.force_cl, self.args.force_rev)
print('Force reviewing change {}'.format(c))
self.process_changes([c])
return
if self.args.stats_file:
try:
with open(self.args.stats_file, 'rt') as f:
self.stats = json.load(f)
except FileNotFoundError:
self.update_stats()
prefixes = ['UPSTREAM', 'BACKPORT', 'FROMGIT', 'FROMLIST']
if self.args.kconfig_hound:
prefixes += ['CHROMIUM']
if self.args.force_prefix:
prefixes = [self.args.force_prefix]
while True:
try:
did_review = 0
for p in prefixes:
changes = self.get_changes(p)
if self.args.verbose:
print('{} changes for prefix {}'.format(len(changes), p))
did_review += self.process_changes(changes)
if did_review > 0:
self.update_stats()
if not self.args.daemon:
break
if self.args.verbose:
print('Finished! Going to sleep until next run')
except (requests.exceptions.HTTPError, OSError) as e:
sys.stderr.write('Error getting changes: ({})\n'.format(str(e)))
time.sleep(60)
time.sleep(120)
def main():
parser = argparse.ArgumentParser(description='Troll gerrit reviews')
parser.add_argument('--git-dir', default=None, help='Path to git directory')
parser.add_argument('--verbose', help='print commits', action='store_true')
parser.add_argument('--chatty', help='print diffs', action='store_true')
parser.add_argument('--daemon', action='store_true',
help='Run in daemon mode, for continuous trolling')
parser.add_argument('--dry-run', action='store_true', default=False,
help='skip the review step')
parser.add_argument('--force-cl', default=None, help='Force review a CL')
parser.add_argument('--force-rev', default=None,
help=('Specify a specific revision of the force-cl to '
'review (ignored if force-cl is not true)'))
parser.add_argument('--force-all', action='store_true', default=False,
help='Force review all (implies dry-run)')
parser.add_argument('--force-prefix', default=None,
help='Only search for the provided prefix')
parser.add_argument('--stats-file', default=None, help='Path to stats file')
parser.add_argument('--kconfig-hound', default=None, action='store_true',
help='Compute and post the total difference for kconfig changes')
args = parser.parse_args()
if args.force_all:
args.dry_run = True
troll = Troll('https://chromium-review.googlesource.com', args)
troll.run()
if __name__ == '__main__':
sys.exit(main())