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""" 19d2793688SJan HøydahlSimple 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 31*c777db7cSJan Høydahlfrom datetime import datetime 32*c777db7cSJan Høydahlfrom time import strftime 33*c777db7cSJan Høydahltry: 34*c777db7cSJan Høydahl from jinja2 import Environment, BaseLoader 35*c777db7cSJan Høydahl can_do_html = True 36*c777db7cSJan Høydahlexcept: 37*c777db7cSJan 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') 42*c777db7cSJan 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 50*c777db7cSJan Høydahl if not (conf.json or conf.html): 51d2793688SJan Høydahl print(text) 52d2793688SJan Høydahl 53*c777db7cSJan Høydahldef make_html(dict): 54*c777db7cSJan Høydahl if not can_do_html: 55*c777db7cSJan Høydahl print ("ERROR: Cannot generate HTML. Please install jinja2") 56*c777db7cSJan Høydahl sys.exit(1) 57*c777db7cSJan Høydahl global conf 58*c777db7cSJan Høydahl template = Environment(loader=BaseLoader).from_string(""" 59*c777db7cSJan Høydahl <h1>Lucene/Solr Github PR report</h1> 60*c777db7cSJan Høydahl 61*c777db7cSJan Høydahl <p>Number of open Pull Requests: {{ open_count }}</p> 62*c777db7cSJan Høydahl 63*c777db7cSJan Høydahl <h2>PRs lacking JIRA reference in title ({{ no_jira_count }})</h2> 64*c777db7cSJan Høydahl <ul> 65*c777db7cSJan Høydahl {% for pr in no_jira %} 66*c777db7cSJan Høydahl <li><a href="https://github.com/apache/lucene-solr/pull/{{ pr.number }}">#{{ pr.number }}: {{ pr.created }} {{ pr.title }}</a> ({{ pr.user }})</li> 67*c777db7cSJan Høydahl {%- endfor %} 68*c777db7cSJan Høydahl </ul> 69*c777db7cSJan Høydahl 70*c777db7cSJan Høydahl <h2>Open PRs with a resolved JIRA ({{ closed_jira_count }})</h2> 71*c777db7cSJan Høydahl <ul> 72*c777db7cSJan Høydahl {% for pr in closed_jira %} 73*c777db7cSJan Høydahl <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> 74*c777db7cSJan Høydahl {%- endfor %} 75*c777db7cSJan Høydahl </ul> 76*c777db7cSJan Høydahl """) 77*c777db7cSJan 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 = {} 89d2793688SJan Høydahl repo = gh.get_repo('apache/lucene-solr') 90d2793688SJan Høydahl open_prs = repo.get_pulls(state='open') 91d2793688SJan Høydahl out("Lucene/Solr 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 96d2793688SJan Høydahl lack_jira = list(filter(lambda x: not re.match(r'.*\b(LUCENE|SOLR)-\d{3,6}\b', x.title), open_prs)) 97*c777db7cSJan Høydahl result['no_jira_count'] = len(lack_jira) 98*c777db7cSJan Høydahl lack_jira_list = [] 99d2793688SJan Høydahl for pr in lack_jira: 100*c777db7cSJan Høydahl lack_jira_list.append({'title': pr.title, 'number': pr.number, 'user': pr.user.login, 'created': pr.created_at.strftime("%Y-%m-%d")}) 101*c777db7cSJan Høydahl result['no_jira'] = lack_jira_list 102d2793688SJan Høydahl out("\nPRs lacking JIRA reference in title") 103*c777db7cSJan Høydahl for pr in lack_jira_list: 104*c777db7cSJan 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") 107d2793688SJan Høydahl has_jira = list(filter(lambda x: re.match(r'.*\b(LUCENE|SOLR)-\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: 112d2793688SJan Høydahl jira_issue_str = re.match(r'.*\b((LUCENE|SOLR)-\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)) 117*c777db7cSJan 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 121*c777db7cSJan Høydahl assignee = issue.fields.assignee.name if issue.fields.assignee else None 122*c777db7cSJan 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, 125*c777db7cSJan Høydahl 'resolution_date': issue.fields.resolutiondate[:10], 126*c777db7cSJan Høydahl 'pr_number': pr_number, 127*c777db7cSJan Høydahl 'pr_title': pr_title, 128*c777db7cSJan Høydahl 'issue_summary': issue.fields.summary, 129*c777db7cSJan Høydahl 'assignee': assignee}) 130*c777db7cSJan Høydahl 131*c777db7cSJan Høydahl closed_jiras.sort(key=lambda r: r['pr_number'], reverse=True) 132*c777db7cSJan Høydahl for issue in closed_jiras: 133*c777db7cSJan Høydahl out(" #%s: %s %s %s: %s (%s)" % (issue['pr_number'], 134*c777db7cSJan Høydahl issue['status'], 135*c777db7cSJan Høydahl issue['resolution_date'], 136*c777db7cSJan Høydahl issue['issue_key'], 137*c777db7cSJan Høydahl issue['issue_summary'], 138*c777db7cSJan Høydahl issue['assignee']) 139d2793688SJan Høydahl ) 140d2793688SJan Høydahl result['closed_jira_count'] = len(resolved_jiras) 141*c777db7cSJan 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 146*c777db7cSJan Høydahl if conf.html: 147*c777db7cSJan 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