Skip to content

Commit 10172a2

Browse files
authored
Merge pull request #3091 from jasongrout/milestonecheck
Adapt the milestone_check script from JupyterLab for ipywidgets.
2 parents a3bc13b + de916f5 commit 10172a2

File tree

1 file changed

+209
-0
lines changed

1 file changed

+209
-0
lines changed

scripts/milestone_check.py

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
# Copyright (c) 2018 Jupyter Development Team.
2+
# Distributed under the terms of the Modified BSD License.
3+
4+
# Generate a GitHub token at https://github.com/settings/tokens
5+
# Invoke this script using something like:
6+
# python scripts/milestone_check.py
7+
8+
# From the milestone_check.py script in JupyterLab
9+
10+
import subprocess
11+
import requests
12+
import os
13+
import sys
14+
15+
REPO = 'jupyter-widgets/ipywidgets'
16+
17+
ranges = {
18+
'8.0': 'origin/master ^7.x',
19+
}
20+
21+
try:
22+
api_token = os.environ['GITHUB_TOKEN']
23+
except KeyError:
24+
print('Error: set the environment variable GITHUB_TOKEN to a GitHub authentication token (see https://github.com/settings/tokens)')
25+
exit(1)
26+
27+
if len(sys.argv) != 2:
28+
print('Error: exactly one argument expected, the milestone.')
29+
exit(1)
30+
31+
MILESTONE=sys.argv[1]
32+
33+
if MILESTONE not in ranges:
34+
print('Error: I do not know about milestone %r. Possible milestones are %r'%(MILESTONE, list(ranges.keys())))
35+
exit(1)
36+
37+
38+
out = subprocess.run("git log {} --format='%H,%cE,%s'".format(ranges[MILESTONE]), shell=True, encoding='utf8', stdout=subprocess.PIPE)
39+
commits = {i[0]: (i[1], i[2]) for i in (x.split(',',2) for x in out.stdout.splitlines())}
40+
41+
42+
url = 'https://api.github.com/graphql'
43+
json = { 'query' : """
44+
query test($cursor: String) {
45+
search(first: 50, after: $cursor, type: ISSUE, query: "repo:%(repo)s milestone:%(milestone)s is:pr is:merged ") {
46+
issueCount
47+
pageInfo {
48+
endCursor
49+
hasNextPage
50+
}
51+
nodes {
52+
... on PullRequest {
53+
title
54+
number
55+
mergeCommit {
56+
oid
57+
}
58+
commits(first: 100) {
59+
totalCount
60+
nodes {
61+
commit {
62+
oid
63+
}
64+
}
65+
}
66+
}
67+
}
68+
}
69+
}
70+
"""%{'milestone': MILESTONE, 'repo': REPO},
71+
'variables': {
72+
'cursor': None
73+
}
74+
}
75+
76+
77+
78+
headers = {'Authorization': 'token %s' % api_token}
79+
# construct a commit to PR dictionary
80+
prs = {}
81+
large_prs = []
82+
cursor = None
83+
while True:
84+
json['variables']['cursor'] = cursor
85+
r = requests.post(url=url, json=json, headers=headers)
86+
results = r.json()['data']['search']
87+
total_prs = results['issueCount']
88+
89+
pr_list = results['nodes']
90+
for pr in pr_list:
91+
if pr['commits']['totalCount'] > 100:
92+
large_prs.append(pr['number'])
93+
continue
94+
# TODO fetch commits
95+
prs[pr['number']] = {'mergeCommit': pr['mergeCommit']['oid'],
96+
'commits': set(i['commit']['oid'] for i in pr['commits']['nodes'])}
97+
98+
has_next_page = results['pageInfo']['hasNextPage']
99+
cursor = results['pageInfo']['endCursor']
100+
101+
if not has_next_page:
102+
break
103+
104+
prjson = {'query': """
105+
query test($pr:Int!, $cursor: String) {
106+
repository(owner: "%(repoowner)s", name: "%(reponame)s") {
107+
pullRequest(number: $pr) {
108+
title
109+
number
110+
mergeCommit {
111+
oid
112+
}
113+
commits(first: 100, after: $cursor) {
114+
totalCount
115+
pageInfo {
116+
endCursor
117+
hasNextPage
118+
}
119+
nodes {
120+
commit {
121+
oid
122+
}
123+
}
124+
}
125+
}
126+
}
127+
}
128+
"""%{'repoowner': REPO.split('/')[0], 'reponame': REPO.split('/')[1]}, 'variables': {
129+
'pr': None,
130+
'cursor': None
131+
}}
132+
133+
for prnumber in large_prs:
134+
prjson['variables']['pr']=prnumber
135+
pr_commits = set()
136+
while True:
137+
r = requests.post(url=url, json=prjson, headers=headers)
138+
pr = r.json()['data']['repository']['pullRequest']
139+
assert pr['number']==prnumber
140+
total_commits = pr['commits']['totalCount']
141+
pr_commits.update(i['commit']['oid'] for i in pr['commits']['nodes'])
142+
has_next_page = results['pageInfo']['hasNextPage']
143+
cursor = results['pageInfo']['endCursor']
144+
145+
if not pr['commits']['pageInfo']['hasNextPage']:
146+
break
147+
prjson['variables']['cursor'] = pr['commits']['pageInfo']['endCursor']
148+
149+
prs[prnumber] = {'mergeCommit': pr['mergeCommit']['oid'],
150+
'commits': pr_commits}
151+
if total_commits > len(pr_commits):
152+
print("WARNING: PR %d (merge %s) has %d commits, but GitHub is only giving us %d of them"%(prnumber, pr['mergeCommit']['oid'], total_commits, len(pr_commits)))
153+
154+
155+
156+
# Check we got all PRs
157+
assert len(prs) == total_prs
158+
159+
# Reverse dictionary
160+
commits_to_prs={}
161+
for key,value in prs.items():
162+
commits_to_prs[value['mergeCommit']]=key
163+
for c in value['commits']:
164+
commits_to_prs[c]=key
165+
166+
# Check to see if commits in the repo are represented in PRs
167+
good = set()
168+
notfound = set()
169+
for c in commits:
170+
if c in commits_to_prs:
171+
good.add(commits_to_prs[c])
172+
else:
173+
notfound.add(c)
174+
175+
prs_not_represented = set(prs.keys()) - good
176+
177+
print("Milestone: %s, %d merged PRs, %d commits in history"%(MILESTONE, total_prs, len(commits)))
178+
179+
print()
180+
print('-'*40)
181+
print()
182+
183+
if len(prs_not_represented) > 0:
184+
print("""
185+
PRs that are in the milestone, but have no commits in the version range.
186+
These PRs probably belong in a different milestone.
187+
""")
188+
print('\n'.join(f'https://github.com/{REPO}/pull/{i}' for i in prs_not_represented))
189+
else:
190+
print('Congratulations! All PRs in this milestone have commits in the commit history for this version range, so they all probably belong in this milestone.')
191+
192+
print()
193+
print('-'*40)
194+
print()
195+
196+
if len(notfound):
197+
print("""The following commits are not included in any PR on this milestone.
198+
This probably means the commit's PR needs to be assigned to this milestone,
199+
or the commit was pushed to master directly.
200+
""")
201+
print('\n'.join('%s %s %s'%(c, commits[c][0], commits[c][1]) for c in notfound))
202+
prs_to_check = [c for c in notfound if 'Merge pull request #' in commits[c][1] and commits[c][0] == '[email protected]']
203+
if len(prs_to_check)>0:
204+
print()
205+
print("Try checking these PRs. They probably should be in the milestone, but probably aren't:")
206+
print()
207+
print('\n'.join('%s %s'%(c, commits[c][1]) for c in prs_to_check))
208+
else:
209+
print('Congratulations! All commits in the commit history are included in some PR in this milestone.')

0 commit comments

Comments
 (0)