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