Skip to content

Commit 98512af

Browse files
committed
refactoring, more manual commands, comments on pr actions
1 parent 09f827c commit 98512af

File tree

3 files changed

+152
-27
lines changed

3 files changed

+152
-27
lines changed

README.md

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@ The file `.gitconsensus.yaml` needs to be placed in the repository to be managed
99
will be skipped.
1010

1111
```yaml
12-
# minimum number of votes
12+
13+
# Add extra labels for the vote counts and age when merging
14+
extra_labels: false
15+
16+
# Minimum number of votes
1317
quorum: 5
1418

1519
# Required percentage of "yes" votes
@@ -34,7 +38,9 @@ mergedelay: 3
3438
timeout: 30
3539
```
3640
37-
## Authentication
41+
## Commands
42+
43+
### Authentication
3844
3945
```shell
4046
gitconsensus auth
@@ -43,22 +49,46 @@ gitconsensus auth
4349
You will be asked for your username, password, and 2fa token (if configured). This will be used to get an authentication
4450
token from Github that will be used in place of your username and password (which are never saved).
4551

46-
## Merge
52+
### Merge
4753

4854
Merge all pull requests that meet consensus rules.
4955

5056
```shell
5157
gitconsensus merge USERNAME REPOSITORY
5258
```
5359

54-
## Close
60+
### Close
5561

5662
Close all pull requests that have passed the "timeout" date (if it is set).
5763

5864
```shell
5965
gitconsensus close USERNAME REPOSITORY
6066
```
6167

68+
### Info
69+
70+
Get detailed infromation about a specific pull request and what rules it passes.
71+
72+
```shell
73+
gitconsensus info USERNAME REPOSITORY PR_NUMBER
74+
```
75+
76+
### Force Close
77+
78+
Close specific pull request, including any labels and comments that normally would be sent.
79+
80+
```shell
81+
gitconsensus forceclose USERNAME REPOSITORY PR_NUMBER
82+
```
83+
84+
### Force Merge
85+
86+
Merge specific pull request, including any labels and comments that normally would be sent.
87+
88+
```shell
89+
gitconsensus forcemerge USERNAME REPOSITORY PR_NUMBER
90+
```
91+
6292
## Label Overrides
6393

64-
Any Pull Request with the `WIP` or `DONTMERGE` label (case insensitive) will be skipped over.
94+
Any Pull Request with a `WIP` or `DONTMERGE` label (case insensitive) will be skipped over.

gitconsensus/gitconsensus

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ def cli(ctx):
1313
print(ctx.parent.get_help())
1414

1515

16-
@cli.command(short_help="obtain an authorization token")
16+
@cli.command(short_help="Obtain an authorization token")
1717
def auth():
1818
username = click.prompt('Username')
1919
password = click.prompt('Password', hide_input=True)
@@ -31,7 +31,7 @@ def auth():
3131
fd.write(auth.token + '\n')
3232

3333

34-
@cli.command(short_help="list open pull requests and their status")
34+
@cli.command(short_help="List open pull requests and their status")
3535
@click.argument('username')
3636
@click.argument('repository_name')
3737
def list(username, repository_name):
@@ -41,7 +41,45 @@ def list(username, repository_name):
4141
click.echo("PR#%s: %s" % (request.number, request.validate()))
4242

4343

44-
@cli.command(short_help="merge open pull requests that validate")
44+
@cli.command(short_help="Display detailed information about a specific pull request")
45+
@click.argument('username')
46+
@click.argument('repository_name')
47+
@click.argument('pull_request')
48+
def info(username, repository_name, pull_request):
49+
repo = Repository(username, repository_name)
50+
request = repo.getPullRequest(pull_request)
51+
click.echo("PR#%s: %s" % (request.number, request.pr.title))
52+
consensus = repo.getConsensus()
53+
click.echo("Mergeable: %s" % (consensus.isMergeable(request),))
54+
click.echo("Is Blocked: %s" % (request.isBlocked(),))
55+
click.echo("Has Quorum: %s" % (consensus.hasQuorum(request),))
56+
click.echo("Has Votes: %s" % (consensus.hasVotes(request),))
57+
click.echo("Has Aged: %s" % (consensus.hasAged(request),))
58+
59+
60+
@cli.command(short_help="Forced a specific pull request to be merged")
61+
@click.argument('username')
62+
@click.argument('repository_name')
63+
@click.argument('pull_request')
64+
def forcemerge(username, repository_name, pull_request):
65+
repo = Repository(username, repository_name)
66+
request = repo.getPullRequest(pull_request)
67+
click.echo("PR#%s: %s" % (request.number, request.pr.title))
68+
request.vote_merge()
69+
70+
71+
@cli.command(short_help="Forced a specific pull request to be closed")
72+
@click.argument('username')
73+
@click.argument('repository_name')
74+
@click.argument('pull_request')
75+
def forcemerge(username, repository_name, pull_request):
76+
repo = Repository(username, repository_name)
77+
request = repo.getPullRequest(pull_request)
78+
click.echo("PR#%s: %s" % (request.number, request.pr.title))
79+
request.close()
80+
81+
82+
@cli.command(short_help="Merge open pull requests that validate")
4583
@click.argument('username')
4684
@click.argument('repository_name')
4785
def merge(username, repository_name):
@@ -53,7 +91,7 @@ def merge(username, repository_name):
5391
request.vote_merge()
5492

5593

56-
@cli.command(short_help="close older unmerged opened pull requests")
94+
@cli.command(short_help="Close older unmerged opened pull requests")
5795
@click.argument('username')
5896
@click.argument('repository_name')
5997
def close(username, repository_name):
@@ -64,7 +102,7 @@ def close(username, repository_name):
64102
continue
65103
if request.shouldClose():
66104
click.echo("Closing PR#%s" % (request.number,))
67-
request.pr.close()
105+
request.close()
68106

69107

70108

gitconsensus/repository.py

Lines changed: 74 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ def getPullRequests(self):
4040
retpr.append(newpr)
4141
return retpr
4242

43+
def getPullRequest(self, number):
44+
return PullRequest(self, number)
45+
4346
def isContributor(self, username):
4447
if not self.contributors:
4548
contributor_list = self.repository.iter_contributors()
@@ -51,9 +54,12 @@ def isCollaborator(username):
5154
self.collaborators[username] = self.repository.is_collaborator(username)
5255
return self.repository.is_collaborator(username)
5356

57+
def getConsensus(self):
58+
return Consensus(self.rules)
5459

55-
class PullRequest:
5660

61+
class PullRequest:
62+
labels = False
5763
def __init__(self, repository, number):
5864
self.repository = repository
5965
self.number = number
@@ -84,7 +90,6 @@ def __init__(self, repository, number):
8490
if username not in self.repository.rules['whitelist']:
8591
continue
8692

87-
8893
if content == '+1':
8994
self.yes.append(user['login'])
9095
elif content == '-1':
@@ -107,13 +112,26 @@ def daysSinceLastCommit(self):
107112
delta = commit_date - now
108113
return delta.days
109114

115+
def daysSincePullOpened(self):
116+
now = datetime.datetime.now()
117+
delta = self.pr.created_at.replace(tzinfo=None) - now
118+
return delta.days
119+
120+
def daysSinceLastUpdate(self):
121+
daysOpen = self.daysSincePullOpened()
122+
daysSinceCommit = self.daysSinceLastCommit()
123+
124+
if daysOpen < daysSinceCommit:
125+
return daysOpen
126+
return daysSinceCommit
127+
110128
def getIssue(self):
111129
return self.repository.repository.issue(self.number)
112130

113131
def validate(self):
114132
if self.repository.rules == False:
115133
return False
116-
consenttest = Consensus(self.repository.rules)
134+
consenttest = self.repository.getConsensus()
117135
return consenttest.validate(self)
118136

119137
def shouldClose(self):
@@ -122,30 +140,66 @@ def shouldClose(self):
122140
return True
123141
return False
124142

143+
def close(self):
144+
self.pr.close()
145+
message = """
146+
This Pull Request has been closed by [GitConsensus](https://github.com/tedivm/GitConsensus).
147+
148+
|||
149+
| ------ | --- |
150+
| Yes | %s |
151+
| No | %s |
152+
| Total | %s |
153+
154+
""" % (str(len(self.yes)), str(len(self.no)), str(len(self.users)))
155+
self.addComment(message)
156+
157+
125158
def vote_merge(self):
126-
self.addLabels([
127-
'gc-merged',
128-
'gc-voters %s' % (len(self.users),),
129-
'gc-yes %s' % (len(self.yes),),
130-
'gc-no %s' % (len(self.no),),
131-
'gc-age %s' % (self.daysSinceLastCommit(),)
132-
])
159+
133160
self.pr.merge('Consensus Merge')
161+
self.addLabels(['gc-merged'])
162+
163+
if 'extra_labels' in self.repository.rules and self.repository.rules['extra_labels']:
164+
self.addLabels([
165+
'gc-voters %s' % (len(self.users),),
166+
'gc-yes %s' % (len(self.yes),),
167+
'gc-no %s' % (len(self.no),),
168+
'gc-age %s' % (self.daysSinceLastUpdate(),)
169+
])
170+
171+
message = """
172+
This Pull Request has been merged by [GitConsensus](https://github.com/tedivm/GitConsensus).
173+
174+
|||
175+
| ------ | --- |
176+
| Yes | %s |
177+
| No | %s |
178+
| Total | %s |
179+
180+
""" % (str(len(self.yes)), str(len(self.no)), str(len(self.users)))
181+
self.addComment(message)
182+
134183

135184
def addLabels(self, labels):
136185
issue = self.getIssue()
137186
for label in labels:
138187
issue.add_labels(label)
139188

189+
def addComment(self, comment_string):
190+
return self.getIssue().create_comment(comment_string)
191+
140192
def getLabelList(self):
141-
issue = self.getIssue()
142-
return issue.labels
193+
if not self.labels:
194+
issue = self.getIssue()
195+
self.labels = [item.name for item in issue.labels]
196+
return self.labels
143197

144-
def isBlocked(self, pr):
198+
def isBlocked(self):
145199
labels = [item.lower() for item in self.getLabelList()]
146200
if 'wip' in labels:
147201
return True
148-
if 'dontmerge':
202+
if 'dontmerge' in labels:
149203
return True
150204
return False
151205

@@ -180,14 +234,17 @@ def hasQuorum(self, pr):
180234

181235
def hasVotes(self, pr):
182236
if 'threshold' in self.rules:
183-
ratio = len(pr.yes) / (len(pr.yes) + len(pr.no))
237+
total = (len(pr.yes) + len(pr.no))
238+
if total <= 0:
239+
return False
240+
ratio = len(pr.yes) / total
184241
if ratio < self.rules['threshold']:
185242
return False
186243
return True
187244

188245
def hasAged(self, pr):
189246
if 'mergedelay' in self.rules:
190-
days = pr.daysSinceLastCommit()
191-
if days < self.rules['mergdelay']:
247+
days = pr.daysSinceLastUpdate()
248+
if days < self.rules['mergedelay']:
192249
return False
193250
return True

0 commit comments

Comments
 (0)