2019-06-14 07:30:04 -04:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
# Licensed to the Apache Software Foundation (ASF) under one or more
|
|
|
|
# contributor license agreements. See the NOTICE file distributed with
|
|
|
|
# this work for additional information regarding copyright ownership.
|
|
|
|
# The ASF licenses this file to You under the Apache License, Version 2.0
|
|
|
|
# (the "License"); you may not use this file except in compliance with
|
|
|
|
# the License. You may obtain a copy of the License at
|
|
|
|
#
|
|
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
#
|
|
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
# See the License for the specific language governing permissions and
|
|
|
|
# limitations under the License.
|
|
|
|
|
|
|
|
"""
|
2021-04-28 13:19:34 -04:00
|
|
|
Simple script that queries GitHub for all open PRs, then finds the ones without
|
2019-06-14 07:30:04 -04:00
|
|
|
issue number in title, and the ones where the linked JIRA is already closed
|
|
|
|
"""
|
|
|
|
|
|
|
|
import os
|
|
|
|
import sys
|
|
|
|
sys.path.append(os.path.dirname(__file__))
|
|
|
|
import argparse
|
|
|
|
import json
|
|
|
|
import re
|
|
|
|
from github import Github
|
|
|
|
from jira import JIRA
|
2020-02-14 20:06:02 -05:00
|
|
|
from datetime import datetime
|
|
|
|
from time import strftime
|
|
|
|
try:
|
|
|
|
from jinja2 import Environment, BaseLoader
|
|
|
|
can_do_html = True
|
|
|
|
except:
|
|
|
|
can_do_html = False
|
2019-06-14 07:30:04 -04:00
|
|
|
|
|
|
|
def read_config():
|
|
|
|
parser = argparse.ArgumentParser(description='Find open Pull Requests that need attention')
|
|
|
|
parser.add_argument('--json', action='store_true', default=False, help='Output as json')
|
2020-02-14 20:06:02 -05:00
|
|
|
parser.add_argument('--html', action='store_true', default=False, help='Output as html')
|
2019-06-14 07:30:04 -04:00
|
|
|
parser.add_argument('--token', help='Github access token in case you query too often anonymously')
|
|
|
|
newconf = parser.parse_args()
|
|
|
|
return newconf
|
|
|
|
|
|
|
|
|
|
|
|
def out(text):
|
|
|
|
global conf
|
2020-02-14 20:06:02 -05:00
|
|
|
if not (conf.json or conf.html):
|
2019-06-14 07:30:04 -04:00
|
|
|
print(text)
|
|
|
|
|
2020-02-14 20:06:02 -05:00
|
|
|
def make_html(dict):
|
|
|
|
if not can_do_html:
|
|
|
|
print ("ERROR: Cannot generate HTML. Please install jinja2")
|
|
|
|
sys.exit(1)
|
|
|
|
global conf
|
|
|
|
template = Environment(loader=BaseLoader).from_string("""
|
|
|
|
<h1>Lucene/Solr Github PR report</h1>
|
|
|
|
|
|
|
|
<p>Number of open Pull Requests: {{ open_count }}</p>
|
|
|
|
|
|
|
|
<h2>PRs lacking JIRA reference in title ({{ no_jira_count }})</h2>
|
|
|
|
<ul>
|
|
|
|
{% for pr in no_jira %}
|
|
|
|
<li><a href="https://github.com/apache/lucene-solr/pull/{{ pr.number }}">#{{ pr.number }}: {{ pr.created }} {{ pr.title }}</a> ({{ pr.user }})</li>
|
|
|
|
{%- endfor %}
|
|
|
|
</ul>
|
|
|
|
|
|
|
|
<h2>Open PRs with a resolved JIRA ({{ closed_jira_count }})</h2>
|
|
|
|
<ul>
|
|
|
|
{% for pr in closed_jira %}
|
|
|
|
<li><a href="https://github.com/apache/lucene-solr/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>
|
|
|
|
{%- endfor %}
|
|
|
|
</ul>
|
|
|
|
""")
|
|
|
|
return template.render(dict)
|
2019-06-14 07:30:04 -04:00
|
|
|
|
|
|
|
def main():
|
|
|
|
global conf
|
|
|
|
conf = read_config()
|
|
|
|
token = conf.token if conf.token is not None else None
|
|
|
|
if token:
|
|
|
|
gh = Github(token)
|
|
|
|
else:
|
|
|
|
gh = Github()
|
|
|
|
jira = JIRA('https://issues.apache.org/jira')
|
|
|
|
result = {}
|
|
|
|
repo = gh.get_repo('apache/lucene-solr')
|
|
|
|
open_prs = repo.get_pulls(state='open')
|
|
|
|
out("Lucene/Solr Github PR report")
|
|
|
|
out("============================")
|
|
|
|
out("Number of open Pull Requests: %s" % open_prs.totalCount)
|
|
|
|
result['open_count'] = open_prs.totalCount
|
|
|
|
|
|
|
|
lack_jira = list(filter(lambda x: not re.match(r'.*\b(LUCENE|SOLR)-\d{3,6}\b', x.title), open_prs))
|
2020-02-14 20:06:02 -05:00
|
|
|
result['no_jira_count'] = len(lack_jira)
|
|
|
|
lack_jira_list = []
|
2019-06-14 07:30:04 -04:00
|
|
|
for pr in lack_jira:
|
2020-02-14 20:06:02 -05:00
|
|
|
lack_jira_list.append({'title': pr.title, 'number': pr.number, 'user': pr.user.login, 'created': pr.created_at.strftime("%Y-%m-%d")})
|
|
|
|
result['no_jira'] = lack_jira_list
|
2019-06-14 07:30:04 -04:00
|
|
|
out("\nPRs lacking JIRA reference in title")
|
2020-02-14 20:06:02 -05:00
|
|
|
for pr in lack_jira_list:
|
|
|
|
out(" #%s: %s %s (%s)" % (pr['number'], pr['created'], pr['title'], pr['user'] ))
|
2019-06-14 07:30:04 -04:00
|
|
|
|
|
|
|
out("\nOpen PRs with a resolved JIRA")
|
|
|
|
has_jira = list(filter(lambda x: re.match(r'.*\b(LUCENE|SOLR)-\d{3,6}\b', x.title), open_prs))
|
|
|
|
|
|
|
|
issue_ids = []
|
|
|
|
issue_to_pr = {}
|
|
|
|
for pr in has_jira:
|
|
|
|
jira_issue_str = re.match(r'.*\b((LUCENE|SOLR)-\d{3,6})\b', pr.title).group(1)
|
|
|
|
issue_ids.append(jira_issue_str)
|
|
|
|
issue_to_pr[jira_issue_str] = pr
|
|
|
|
|
|
|
|
resolved_jiras = jira.search_issues(jql_str="key in (%s) AND status in ('Closed', 'Resolved')" % ", ".join(issue_ids))
|
2020-02-14 20:06:02 -05:00
|
|
|
closed_jiras = []
|
2019-06-14 07:30:04 -04:00
|
|
|
for issue in resolved_jiras:
|
|
|
|
pr_title = issue_to_pr[issue.key].title
|
|
|
|
pr_number = issue_to_pr[issue.key].number
|
2020-02-14 20:06:02 -05:00
|
|
|
assignee = issue.fields.assignee.name if issue.fields.assignee else None
|
|
|
|
closed_jiras.append({ 'issue_key': issue.key,
|
|
|
|
'status': issue.fields.status.name,
|
|
|
|
'resolution': issue.fields.resolution.name,
|
|
|
|
'resolution_date': issue.fields.resolutiondate[:10],
|
|
|
|
'pr_number': pr_number,
|
|
|
|
'pr_title': pr_title,
|
|
|
|
'issue_summary': issue.fields.summary,
|
|
|
|
'assignee': assignee})
|
|
|
|
|
|
|
|
closed_jiras.sort(key=lambda r: r['pr_number'], reverse=True)
|
|
|
|
for issue in closed_jiras:
|
|
|
|
out(" #%s: %s %s %s: %s (%s)" % (issue['pr_number'],
|
|
|
|
issue['status'],
|
|
|
|
issue['resolution_date'],
|
|
|
|
issue['issue_key'],
|
|
|
|
issue['issue_summary'],
|
|
|
|
issue['assignee'])
|
2019-06-14 07:30:04 -04:00
|
|
|
)
|
|
|
|
result['closed_jira_count'] = len(resolved_jiras)
|
2020-02-14 20:06:02 -05:00
|
|
|
result['closed_jira'] = closed_jiras
|
2019-06-14 07:30:04 -04:00
|
|
|
|
|
|
|
if conf.json:
|
|
|
|
print(json.dumps(result, indent=4))
|
|
|
|
|
2020-02-14 20:06:02 -05:00
|
|
|
if conf.html:
|
|
|
|
print(make_html(result))
|
2019-06-14 07:30:04 -04:00
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
try:
|
|
|
|
main()
|
|
|
|
except KeyboardInterrupt:
|
|
|
|
print('\nReceived Ctrl-C, exiting early')
|