Skip to content

Commit aee22b0

Browse files
committed
Add more info labels, restrict delayoverride to contributors and changes that are not license or consensus based
1 parent 47fbce5 commit aee22b0

File tree

3 files changed

+109
-14
lines changed

3 files changed

+109
-14
lines changed

README.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,22 @@ collaborators_only: false
3333
# When defined only process votes from these github users
3434
whitelist:
3535
- alice
36-
- bob
3736
- carol
3837

38+
# When defined votes from these users will be ignored
39+
blacklist:
40+
- bob
41+
- dan
42+
3943
# Number of hours after last action (commit or opening the pull request) before issue can be merged
4044
mergedelay: 24
4145

42-
# Number of votes at which the mergedelay gets ignored, assuming no negative votes.
46+
# Number of votes from contributors at which the mergedelay gets ignored, assuming no negative votes.
4347
delayoverride: 10
4448

49+
# When `delayoverride` is set this value is the minimum hours without changes before the PR will be merged
50+
mergedelaymin: 1
51+
4552
# Number of hours after last action (commit or opening the pull request) before issue is autoclosed
4653
timeout: 720
4754
```

gitconsensus/gitconsensus.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ def merge(username, repository_name):
8585
repo = Repository(username, repository_name)
8686
requests = repo.getPullRequests()
8787
for request in requests:
88+
request.addInfoLabels()
8889
if request.validate():
8990
click.echo("Merging PR#%s" % (request.number,))
9091
request.vote_merge()
@@ -101,6 +102,7 @@ def close(username, repository_name):
101102
continue
102103
if request.shouldClose():
103104
click.echo("Closing PR#%s" % (request.number,))
105+
request.addInfoLabels()
104106
request.close()
105107

106108
if __name__ == '__main__':

gitconsensus/repository.py

Lines changed: 98 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ def getPullRequest(self, number):
7878
def isContributor(self, username):
7979
if not self.contributors:
8080
contributor_list = self.repository.iter_contributors()
81-
self.contributors = [contributor['login'] for contributor in contributor_list]
81+
self.contributors = [str(contributor) for contributor in contributor_list]
8282
return username in self.contributors
8383

8484
def isCollaborator(username):
@@ -94,6 +94,7 @@ class PullRequest:
9494
labels = False
9595
def __init__(self, repository, number):
9696
self.repository = repository
97+
self.consensus = repository.getConsensus()
9798
self.number = number
9899
self.pr = self.repository.client.pull_request(self.repository.user, self.repository.name, number)
99100

@@ -105,6 +106,11 @@ def __init__(self, repository, number):
105106
self.yes = []
106107
self.no = []
107108
self.abstain = []
109+
110+
self.contributors_yes = []
111+
self.contributors_no = []
112+
self.contributors_abstain = []
113+
108114
self.users = []
109115
self.doubles = []
110116
for reaction in reactions:
@@ -115,6 +121,10 @@ def __init__(self, repository, number):
115121
if username in self.doubles:
116122
continue
117123

124+
if 'blacklist' in self.repository.rules and self.repository.rules['blacklist']:
125+
if username in self.repository.blacklist:
126+
continue
127+
118128
if 'collaborators_only' in self.repository.rules and self.repository.rules['collaborators_only']:
119129
if not isCollaborator(username):
120130
continue
@@ -138,17 +148,38 @@ def __init__(self, repository, number):
138148
self.no.remove(username)
139149
if username in self.abstain:
140150
self.abstain.remove(username)
151+
if username in self.contributors_yes:
152+
self.contributors_yes.remove(username)
153+
if username in self.contributors_no:
154+
self.contributors_no.remove(username)
155+
if username in self.contributors_abstain:
156+
self.contributors_abstain.remove(username)
141157
continue
142158

143159
if content == '+1':
144-
self.yes.append(user['login'])
145160
self.users.append(user['login'])
161+
self.yes.append(user['login'])
162+
if self.repository.isContributor(user['login']):
163+
self.contributors_yes.append(user['login'])
146164
elif content == '-1':
147-
self.no.append(user['login'])
148165
self.users.append(user['login'])
166+
self.no.append(user['login'])
167+
if seld.repository.isContributor(user['login']):
168+
self.contributors_no.append(user['login'])
149169
elif content == 'confused':
150-
self.abstain.append(user['login'])
151170
self.users.append(user['login'])
171+
self.abstain.append(user['login'])
172+
if seld.repository.isContributor(user['login']):
173+
self.contributors_abstain.append(user['login'])
174+
175+
files = self.pr.iter_files()
176+
self.changes_consensus = False
177+
self.changes_license = False
178+
for changed_file in files:
179+
if changed_file.filename == '.gitconsensus.yaml':
180+
self.changes_consensus = True
181+
if changed_file.filename.lower().startswith('license'):
182+
self.changes_license = True
152183

153184
def hoursSinceLastCommit(self):
154185
commits = self.pr.iter_commits()
@@ -175,14 +206,19 @@ def hoursSinceLastUpdate(self):
175206
return hoursOpen
176207
return hoursSinceCommit
177208

209+
def changesConsensus(self):
210+
return self.changes_consensus
211+
212+
def changesLicense(self):
213+
return self.changes_license
214+
178215
def getIssue(self):
179216
return self.repository.repository.issue(self.number)
180217

181218
def validate(self):
182219
if self.repository.rules == False:
183220
return False
184-
consenttest = self.repository.getConsensus()
185-
return consenttest.validate(self)
221+
return self.consensus.validate(self)
186222

187223
def shouldClose(self):
188224
if 'timeout' in self.repository.rules:
@@ -193,11 +229,13 @@ def shouldClose(self):
193229
def close(self):
194230
self.pr.close()
195231
self.addLabels(['gc-closed'])
232+
self.cleanInfoLabels()
196233
self.commentAction('closed')
197234

198235
def vote_merge(self):
199236
self.pr.merge('GitConsensus Merge')
200237
self.addLabels(['gc-merged'])
238+
self.cleanInfoLabels()
201239

202240
if 'extra_labels' in self.repository.rules and self.repository.rules['extra_labels']:
203241
self.addLabels([
@@ -208,18 +246,53 @@ def vote_merge(self):
208246
])
209247
self.commentAction('merged')
210248

249+
def addInfoLabels(self):
250+
labels = self.getLabelList()
251+
252+
licenseMessage = 'License Change'
253+
if self.changesLicense():
254+
self.addLabels([licenseMessage])
255+
else:
256+
self.removeLabels([licenseMessage])
257+
258+
consensusMessage = 'Consensus Change'
259+
if self.changesConsensus():
260+
self.addLabels([consensusMessage])
261+
else:
262+
self.removeLabels([consensusMessage])
263+
264+
hasQuorumMessage = 'Has Quorum'
265+
needsQuorumMessage = 'Needs Votes'
266+
if self.consensus.hasQuorum(self):
267+
self.addLabels([hasQuorumMessage])
268+
self.removeLabels([needsQuorumMessage])
269+
else:
270+
self.removeLabels([hasQuorumMessage])
271+
self.addLabels([needsQuorumMessage])
272+
273+
passingMessage = 'Passing'
274+
failingMessage = 'Failing'
275+
if self.consensus.hasVotes(self):
276+
self.addLabels([passingMessage])
277+
self.removeLabels([failingMessage])
278+
else:
279+
self.removeLabels([passingMessage])
280+
self.addLabels([failingMessage])
281+
282+
def cleanInfoLabels(self):
283+
self.removeLabels(['Failing', 'Passing', 'Needs Votes', 'Has Quorum'])
284+
211285
def commentAction(self, action):
212286
table = self.buildVoteTable()
213-
consensus = self.repository.getConsensus()
214287
message = message_template % (
215288
action,
216289
str(len(self.yes)),
217290
str(len(self.no)),
218291
str(len(self.abstain)),
219292
str(len(self.users)),
220293
table,
221-
consensus.hasQuorum(self),
222-
consensus.hasVotes(self)
294+
self.consensus.hasQuorum(self),
295+
self.consensus.hasVotes(self)
223296
)
224297

225298
if len(self.doubles) > 0:
@@ -251,11 +324,19 @@ def buildVoteTable(self):
251324
table = "%s\n%s" % (table, row)
252325
return table
253326

254-
255327
def addLabels(self, labels):
328+
existing = self.getLabelList()
256329
issue = self.getIssue()
257330
for label in labels:
258-
issue.add_labels(label)
331+
if label not in existing:
332+
issue.add_labels(label)
333+
334+
def removeLabels(self, labels):
335+
existing = self.getLabelList()
336+
issue = self.getIssue()
337+
for label in labels:
338+
if label in existing:
339+
issue.remove_label(label)
259340

260341
def addComment(self, comment_string):
261342
return self.getIssue().create_comment(comment_string)
@@ -320,8 +401,13 @@ def hasAged(self, pr):
320401
if hours >= self.rules['mergedelay']:
321402
return True
322403
if 'delayoverride' in self.rules and self.rules['delayoverride']:
404+
if pr.changesConsensus() or pr.changesLicense():
405+
return False
406+
if 'mergedelaymin' in self.rules and self.rules['mergedelaymin']:
407+
if hours < self.rules['mergedelaymin']:
408+
return False
323409
if len(pr.no) > 0:
324410
return False
325-
if len(pr.yes) >= self.rules['delayoverride']:
411+
if len(pr.contributors_yes) >= self.rules['delayoverride']:
326412
return True
327413
return False

0 commit comments

Comments
 (0)