4 # Do we have any open PR's that have label "Approval: done"
5 # that are over 24 hours without any other comments?
7 # get a token.... https://github.com/settings/tokens/new -- just repo is fine
8 # pop it in token.txt or you'll get a bad API limit
10 # note that we'd use pyGithub but we can't as it doesn't fully handle the timeline objects
13 # mark@openssl.org Feb 2020
17 from datetime import datetime, timezone
18 from optparse import OptionParser
20 api_url = "https://api.github.com/repos/openssl/openssl"
22 def convertdate(date):
23 # python fromisoformat needs a TZ in hours/minutes
24 return datetime.fromisoformat(date.replace('Z', '+00:00'))
26 # Get all the open pull requests, filtering by approval: done label
28 def getpullrequests():
29 url = api_url + "/pulls?per_page=100&page=1" # defaults to open
30 res = requests.get(url, headers=headers)
33 while 'next' in res.links.keys():
34 res = requests.get(res.links['next']['url'], headers=headers)
35 repos.extend(res.json())
37 # Let's filter by label if we're just looking to move things, we can parse
38 # everything for statistics in another script
43 for label in pr['labels']:
44 if label['name'] == 'approval: done':
45 prs.append(pr['number'])
47 print("failed", repos['message'])
50 # Change the labels on an issue from approval: done to approval: ready to merge
52 def movelabeldonetoready(issue):
53 url = api_url + "/issues/" + str(issue) + "/labels/approval:%20done"
54 res = requests.delete(url, headers=headers)
55 if (res.status_code != 200):
56 print("Error removing label", res.status_code, res.content)
58 url = api_url + "/issues/" + str(issue) + "/labels"
59 newlabel = {"labels": ["approval: ready to merge"]}
60 res = requests.post(url, data=json.dumps(newlabel), headers=headers)
61 if (res.status_code != 200):
62 print("Error adding label", res.status_code, res.content)
64 # Check through an issue and see if it's a candidate for moving
67 url = api_url + "/issues/" + str(pr) + "/timeline?per_page=100&page=1"
68 res = requests.get(url, headers=headers)
70 while 'next' in res.links.keys():
71 res = requests.get(res.links['next']['url'], headers=headers)
72 repos.extend(res.json())
80 if (event['event'] == "commented"):
81 comments.append(convertdate(event["updated_at"]))
83 print("debug: commented at ",
84 convertdate(event["updated_at"]))
85 if (event['event'] == "committed"):
86 comments.append(convertdate(event["author"]["date"]))
88 print("debug: created at ",
89 convertdate(event["author"]["date"]))
90 elif (event['event'] == "labeled"):
92 print("debug: labelled with ", event['label']['name'],
93 "at", convertdate(event["created_at"]))
94 approvallabel[event['label']['name']] = convertdate(
96 elif (event['event'] == "unlabeled"):
98 print("debug: unlabelled with ", event['label']['name'],
99 "at", convertdate(event["created_at"]))
101 'name'] in approvallabel: # have to do this for if labels got renamed in the middle
102 del approvallabel[event['label']['name']]
103 elif (event['event'] == "reviewed"
104 and event['state'] == "approved"):
106 print("debug: approved at",
107 convertdate(event['submitted_at']))
109 return (repos['message'])
111 if 'approval: ready to merge' in approvallabel:
112 return ("issue already has label approval: ready to merge")
113 if 'approval: done' not in approvallabel:
114 return ("issue did not get label approval: done")
115 approvedone = approvallabel['approval: done']
117 if max(comments) > approvedone:
118 return ("issue had comments after approval: done label was given")
120 now = datetime.now(timezone.utc)
121 hourssinceapproval = (now - approvedone).total_seconds() / 3600
124 print("Last comment: ", max(comments))
125 print("Approved since: ", approvedone)
126 print("hours since approval", hourssinceapproval)
128 if (hourssinceapproval < 24):
129 return ("not yet 24 hours since labelled approval:done hours:" +
130 str(int(hourssinceapproval)))
133 print("Moving issue ", pr, " to approval: ready to merge")
134 movelabeldonetoready(pr)
136 print("use --commit to actually change the labels")
138 "this issue was candidate to move to approval: ready to merge hours:" +
139 str(int(hourssinceapproval)))
143 parser = OptionParser()
144 parser.add_option("-d","--debug",action="store_true",help="be noisy",dest="debug")
145 parser.add_option("-t","--token",help="file containing github authentication token for example 'token 18asdjada...'",dest="token")
146 parser.add_option("-c","--commit",action="store_true",help="actually change the labels",dest="commit")
147 (options, args) = parser.parse_args()
149 fp = open(options.token, "r")
150 git_token = fp.readline().strip('\n')
152 git_token = "" # blank token is fine, but you can't change labels and you hit API rate limiting
153 debug = options.debug
154 # since timeline is a preview feature we have to enable access to it with an accept header
156 "Accept": "application/vnd.github.mockingbird-preview",
157 "Authorization": git_token
161 print("Getting list of PRs")
162 prs = getpullrequests()
163 print("There were", len(prs), "open PRs with approval:done ")
165 print(pr, checkpr(pr))