xref: /Lucene/dev-tools/scripts/githubPRs.py (revision 674b66dd16f5b89b7c97ea9aedcac077e6d7f3b9)
1d2793688SJan Høydahl#!/usr/bin/env python3
2d2793688SJan Høydahl# -*- coding: utf-8 -*-
3d2793688SJan Høydahl# Licensed to the Apache Software Foundation (ASF) under one or more
4d2793688SJan Høydahl# contributor license agreements.  See the NOTICE file distributed with
5d2793688SJan Høydahl# this work for additional information regarding copyright ownership.
6d2793688SJan Høydahl# The ASF licenses this file to You under the Apache License, Version 2.0
7d2793688SJan Høydahl# (the "License"); you may not use this file except in compliance with
8d2793688SJan Høydahl# the License.  You may obtain a copy of the License at
9d2793688SJan Høydahl#
10d2793688SJan Høydahl#     http://www.apache.org/licenses/LICENSE-2.0
11d2793688SJan Høydahl#
12d2793688SJan Høydahl# Unless required by applicable law or agreed to in writing, software
13d2793688SJan Høydahl# distributed under the License is distributed on an "AS IS" BASIS,
14d2793688SJan Høydahl# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15d2793688SJan Høydahl# See the License for the specific language governing permissions and
16d2793688SJan Høydahl# limitations under the License.
17d2793688SJan Høydahl
18d2793688SJan Høydahl"""
19c49bfb8eSAyushman Singh ChauhanSimple script that queries GitHub for all open PRs, then finds the ones without
20d2793688SJan Høydahlissue number in title, and the ones where the linked JIRA is already closed
21d2793688SJan Høydahl"""
22d2793688SJan Høydahl
23d2793688SJan Høydahlimport os
24d2793688SJan Høydahlimport sys
25d2793688SJan Høydahlsys.path.append(os.path.dirname(__file__))
26d2793688SJan Høydahlimport argparse
27d2793688SJan Høydahlimport json
28d2793688SJan Høydahlimport re
29d2793688SJan Høydahlfrom github import Github
30d2793688SJan Høydahlfrom jira import JIRA
31c777db7cSJan Høydahlfrom datetime import datetime
32c777db7cSJan Høydahlfrom time import strftime
33c777db7cSJan Høydahltry:
34c777db7cSJan Høydahl  from jinja2 import Environment, BaseLoader
35c777db7cSJan Høydahl  can_do_html = True
36c777db7cSJan Høydahlexcept:
37c777db7cSJan Høydahl  can_do_html = False
38d2793688SJan Høydahl
39d2793688SJan Høydahldef read_config():
40d2793688SJan Høydahl  parser = argparse.ArgumentParser(description='Find open Pull Requests that need attention')
41d2793688SJan Høydahl  parser.add_argument('--json', action='store_true', default=False, help='Output as json')
42c777db7cSJan Høydahl  parser.add_argument('--html', action='store_true', default=False, help='Output as html')
43d2793688SJan Høydahl  parser.add_argument('--token', help='Github access token in case you query too often anonymously')
44d2793688SJan Høydahl  newconf = parser.parse_args()
45d2793688SJan Høydahl  return newconf
46d2793688SJan Høydahl
47d2793688SJan Høydahl
48d2793688SJan Høydahldef out(text):
49d2793688SJan Høydahl  global conf
50c777db7cSJan Høydahl  if not (conf.json or conf.html):
51d2793688SJan Høydahl    print(text)
52d2793688SJan Høydahl
53c777db7cSJan Høydahldef make_html(dict):
54c777db7cSJan Høydahl  if not can_do_html:
55c777db7cSJan Høydahl    print ("ERROR: Cannot generate HTML. Please install jinja2")
56c777db7cSJan Høydahl    sys.exit(1)
57c777db7cSJan Høydahl  global conf
58c777db7cSJan Høydahl  template = Environment(loader=BaseLoader).from_string("""
59*674b66ddSJan Høydahl  <h1>Lucene Github PR report</h1>
60c777db7cSJan Høydahl
61c777db7cSJan Høydahl  <p>Number of open Pull Requests: {{ open_count }}</p>
62c777db7cSJan Høydahl
63c777db7cSJan Høydahl  <h2>PRs lacking JIRA reference in title ({{ no_jira_count }})</h2>
64c777db7cSJan Høydahl  <ul>
65c777db7cSJan Høydahl  {% for pr in no_jira %}
66*674b66ddSJan Høydahl    <li><a href="https://github.com/apache/lucene/pull/{{ pr.number }}">#{{ pr.number }}: {{ pr.created }} {{ pr.title }}</a> ({{ pr.user }})</li>
67c777db7cSJan Høydahl  {%- endfor %}
68c777db7cSJan Høydahl  </ul>
69c777db7cSJan Høydahl
70c777db7cSJan Høydahl  <h2>Open PRs with a resolved JIRA ({{ closed_jira_count }})</h2>
71c777db7cSJan Høydahl  <ul>
72c777db7cSJan Høydahl  {% for pr in closed_jira %}
73*674b66ddSJan Høydahl    <li><a href="https://github.com/apache/lucene/pull/{{ pr.pr_number }}">#{{ pr.pr_number }}</a>: <a href="https://issues.apache.org/jira/browse/{{ pr.issue_key }}">{{ pr.status }} {{ pr.resolution_date }} {{ pr.issue_key}}: {{ pr.issue_summary }}</a> ({{ pr.assignee }})</li>
74c777db7cSJan Høydahl  {%- endfor %}
75c777db7cSJan Høydahl  </ul>
76c777db7cSJan Høydahl  """)
77c777db7cSJan Høydahl  return template.render(dict)
78d2793688SJan Høydahl
79d2793688SJan Høydahldef main():
80d2793688SJan Høydahl  global conf
81d2793688SJan Høydahl  conf = read_config()
82d2793688SJan Høydahl  token = conf.token if conf.token is not None else None
83d2793688SJan Høydahl  if token:
84d2793688SJan Høydahl    gh = Github(token)
85d2793688SJan Høydahl  else:
86d2793688SJan Høydahl    gh = Github()
87d2793688SJan Høydahl  jira = JIRA('https://issues.apache.org/jira')
88d2793688SJan Høydahl  result = {}
89*674b66ddSJan Høydahl  repo = gh.get_repo('apache/lucene')
90d2793688SJan Høydahl  open_prs = repo.get_pulls(state='open')
91*674b66ddSJan Høydahl  out("Lucene Github PR report")
92d2793688SJan Høydahl  out("============================")
93d2793688SJan Høydahl  out("Number of open Pull Requests: %s" % open_prs.totalCount)
94d2793688SJan Høydahl  result['open_count'] = open_prs.totalCount
95d2793688SJan Høydahl
96*674b66ddSJan Høydahl  lack_jira = list(filter(lambda x: not re.match(r'.*\b(LUCENE)-\d{3,6}\b', x.title), open_prs))
97c777db7cSJan Høydahl  result['no_jira_count'] = len(lack_jira)
98c777db7cSJan Høydahl  lack_jira_list = []
99d2793688SJan Høydahl  for pr in lack_jira:
100c777db7cSJan Høydahl    lack_jira_list.append({'title': pr.title, 'number': pr.number, 'user': pr.user.login, 'created': pr.created_at.strftime("%Y-%m-%d")})
101c777db7cSJan Høydahl  result['no_jira'] = lack_jira_list
102d2793688SJan Høydahl  out("\nPRs lacking JIRA reference in title")
103c777db7cSJan Høydahl  for pr in lack_jira_list:
104c777db7cSJan Høydahl    out("  #%s: %s %s (%s)" % (pr['number'], pr['created'], pr['title'], pr['user'] ))
105d2793688SJan Høydahl
106d2793688SJan Høydahl  out("\nOpen PRs with a resolved JIRA")
107*674b66ddSJan Høydahl  has_jira = list(filter(lambda x: re.match(r'.*\b(LUCENE)-\d{3,6}\b', x.title), open_prs))
108d2793688SJan Høydahl
109d2793688SJan Høydahl  issue_ids = []
110d2793688SJan Høydahl  issue_to_pr = {}
111d2793688SJan Høydahl  for pr in has_jira:
112*674b66ddSJan Høydahl    jira_issue_str = re.match(r'.*\b((LUCENE)-\d{3,6})\b', pr.title).group(1)
113d2793688SJan Høydahl    issue_ids.append(jira_issue_str)
114d2793688SJan Høydahl    issue_to_pr[jira_issue_str] = pr
115d2793688SJan Høydahl
116d2793688SJan Høydahl  resolved_jiras = jira.search_issues(jql_str="key in (%s) AND status in ('Closed', 'Resolved')" % ", ".join(issue_ids))
117c777db7cSJan Høydahl  closed_jiras = []
118d2793688SJan Høydahl  for issue in resolved_jiras:
119d2793688SJan Høydahl    pr_title = issue_to_pr[issue.key].title
120d2793688SJan Høydahl    pr_number = issue_to_pr[issue.key].number
121c777db7cSJan Høydahl    assignee = issue.fields.assignee.name if issue.fields.assignee else None
122c777db7cSJan Høydahl    closed_jiras.append({ 'issue_key': issue.key,
123d2793688SJan Høydahl                           'status': issue.fields.status.name,
124d2793688SJan Høydahl                           'resolution': issue.fields.resolution.name,
125c777db7cSJan Høydahl                           'resolution_date': issue.fields.resolutiondate[:10],
126c777db7cSJan Høydahl                           'pr_number': pr_number,
127c777db7cSJan Høydahl                           'pr_title': pr_title,
128c777db7cSJan Høydahl                           'issue_summary': issue.fields.summary,
129c777db7cSJan Høydahl                           'assignee': assignee})
130c777db7cSJan Høydahl
131c777db7cSJan Høydahl  closed_jiras.sort(key=lambda r: r['pr_number'], reverse=True)
132c777db7cSJan Høydahl  for issue in closed_jiras:
133c777db7cSJan Høydahl    out("  #%s: %s %s %s: %s (%s)" % (issue['pr_number'],
134c777db7cSJan Høydahl                                  issue['status'],
135c777db7cSJan Høydahl                                  issue['resolution_date'],
136c777db7cSJan Høydahl                                  issue['issue_key'],
137c777db7cSJan Høydahl                                  issue['issue_summary'],
138c777db7cSJan Høydahl                                  issue['assignee'])
139d2793688SJan Høydahl        )
140d2793688SJan Høydahl  result['closed_jira_count'] = len(resolved_jiras)
141c777db7cSJan Høydahl  result['closed_jira'] = closed_jiras
142d2793688SJan Høydahl
143d2793688SJan Høydahl  if conf.json:
144d2793688SJan Høydahl    print(json.dumps(result, indent=4))
145d2793688SJan Høydahl
146c777db7cSJan Høydahl  if conf.html:
147c777db7cSJan Høydahl    print(make_html(result))
148d2793688SJan Høydahl
149d2793688SJan Høydahlif __name__ == '__main__':
150d2793688SJan Høydahl  try:
151d2793688SJan Høydahl    main()
152d2793688SJan Høydahl  except KeyboardInterrupt:
153d2793688SJan Høydahl    print('\nReceived Ctrl-C, exiting early')
154