Disables allowing the python argparse library from automatically shortening command line arguments, this prevents issues whereby a new command is added and code that wrongly uses the shortened command of an existing argument which is the same as the new command being added will silently change script behaviour. Signed-off-by: Jamie McCrae <jamie.mccrae@nordicsemi.no>
218 lines
6.8 KiB
Python
Executable file
218 lines
6.8 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
#
|
|
# Copyright (c) 2019 Intel Corporation
|
|
#
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
|
|
# Lists all closes issues since a given date
|
|
|
|
import argparse
|
|
import sys
|
|
import os
|
|
import re
|
|
import time
|
|
import threading
|
|
import requests
|
|
|
|
|
|
args = None
|
|
|
|
|
|
class Spinner:
|
|
busy = False
|
|
delay = 0.1
|
|
|
|
@staticmethod
|
|
def spinning_cursor():
|
|
while 1:
|
|
for cursor in '|/-\\':
|
|
yield cursor
|
|
|
|
def __init__(self, delay=None):
|
|
self.spinner_generator = self.spinning_cursor()
|
|
if delay and float(delay):
|
|
self.delay = delay
|
|
|
|
def spinner_task(self):
|
|
while self.busy:
|
|
sys.stdout.write(next(self.spinner_generator))
|
|
sys.stdout.flush()
|
|
time.sleep(self.delay)
|
|
sys.stdout.write('\b')
|
|
sys.stdout.flush()
|
|
|
|
def __enter__(self):
|
|
self.busy = True
|
|
threading.Thread(target=self.spinner_task).start()
|
|
|
|
def __exit__(self, exception, value, tb):
|
|
self.busy = False
|
|
time.sleep(self.delay)
|
|
if exception is not None:
|
|
return False
|
|
|
|
|
|
class Issues:
|
|
def __init__(self, org, repo, token):
|
|
self.repo = repo
|
|
self.org = org
|
|
self.issues_url = "https://github.com/%s/%s/issues" % (
|
|
self.org, self.repo)
|
|
self.github_url = 'https://api.github.com/repos/%s/%s' % (
|
|
self.org, self.repo)
|
|
|
|
self.api_token = token
|
|
self.headers = {}
|
|
self.headers['Authorization'] = 'token %s' % self.api_token
|
|
self.headers['Accept'] = 'application/vnd.github.golden-comet-preview+json'
|
|
self.items = []
|
|
|
|
def get_pull(self, pull_nr):
|
|
url = ("%s/pulls/%s" % (self.github_url, pull_nr))
|
|
response = requests.get("%s" % (url), headers=self.headers)
|
|
if response.status_code != 200:
|
|
raise RuntimeError(
|
|
"Failed to get issue due to unexpected HTTP status code: {}".format(
|
|
response.status_code)
|
|
)
|
|
item = response.json()
|
|
return item
|
|
|
|
def get_issue(self, issue_nr):
|
|
url = ("%s/issues/%s" % (self.github_url, issue_nr))
|
|
response = requests.get("%s" % (url), headers=self.headers)
|
|
if response.status_code != 200:
|
|
return None
|
|
|
|
item = response.json()
|
|
return item
|
|
|
|
def list_issues(self, url):
|
|
response = requests.get("%s" % (url), headers=self.headers)
|
|
if response.status_code != 200:
|
|
raise RuntimeError(
|
|
"Failed to get issue due to unexpected HTTP status code: {}".format(
|
|
response.status_code)
|
|
)
|
|
self.items = self.items + response.json()
|
|
|
|
try:
|
|
print("Getting more items...")
|
|
next_issues = response.links["next"]
|
|
if next_issues:
|
|
next_url = next_issues['url']
|
|
self.list_issues(next_url)
|
|
except KeyError:
|
|
pass
|
|
|
|
def issues_since(self, date, state="closed"):
|
|
self.list_issues("%s/issues?per_page=100&state=%s&since=%s" %
|
|
(self.github_url, state, date))
|
|
|
|
def pull_requests(self, base='v1.14-branch', state='closed'):
|
|
self.list_issues("%s/pulls?per_page=100&state=%s&base=%s" %
|
|
(self.github_url, state, base))
|
|
|
|
|
|
def parse_args():
|
|
global args
|
|
|
|
parser = argparse.ArgumentParser(
|
|
description=__doc__,
|
|
formatter_class=argparse.RawDescriptionHelpFormatter, allow_abbrev=False)
|
|
|
|
parser.add_argument("-o", "--org", default="zephyrproject-rtos",
|
|
help="Github organisation")
|
|
|
|
parser.add_argument("-r", "--repo", default="zephyr",
|
|
help="Github repository")
|
|
|
|
parser.add_argument("-f", "--file", required=True,
|
|
help="Name of output file.")
|
|
|
|
parser.add_argument("-s", "--issues-since",
|
|
help="""List issues since date where date
|
|
is in the format 2019-09-01.""")
|
|
|
|
parser.add_argument("-b", "--issues-in-pulls",
|
|
help="List issues in pulls for a given branch")
|
|
|
|
parser.add_argument("-c", "--commits-file",
|
|
help="""File with all commits (git log a..b) to
|
|
be parsed for fixed bugs.""")
|
|
|
|
args = parser.parse_args()
|
|
|
|
|
|
def main():
|
|
parse_args()
|
|
|
|
token = os.environ.get('GITHUB_TOKEN', None)
|
|
if not token:
|
|
sys.exit("""Github token not set in environment,
|
|
set the env. variable GITHUB_TOKEN please and retry.""")
|
|
|
|
i = Issues(args.org, args.repo, token)
|
|
|
|
if args.issues_since:
|
|
i.issues_since(args.issues_since)
|
|
count = 0
|
|
with open(args.file, "w") as f:
|
|
for issue in i.items:
|
|
if 'pull_request' not in issue:
|
|
# * :github:`8193` - STM32 config BUILD_OUTPUT_HEX fail
|
|
f.write("* :github:`{}` - {}\n".format(
|
|
issue['number'], issue['title'].strip()))
|
|
count = count + 1
|
|
elif args.issues_in_pulls:
|
|
i.pull_requests(base=args.issues_in_pulls)
|
|
count = 0
|
|
|
|
bugs = set()
|
|
backports = []
|
|
for issue in i.items:
|
|
if not isinstance(issue['body'], str):
|
|
continue
|
|
match = re.findall(r"(Fixes|Closes|Fixed|close):? #([0-9]+)",
|
|
issue['body'], re.MULTILINE)
|
|
if match:
|
|
for mm in match:
|
|
bugs.add(mm[1])
|
|
else:
|
|
match = re.findall(
|
|
r"Backport #([0-9]+)", issue['body'], re.MULTILINE)
|
|
if match:
|
|
backports.append(match[0])
|
|
|
|
# follow PRs to their origin (backports)
|
|
with Spinner():
|
|
for p in backports:
|
|
item = i.get_pull(p)
|
|
match = re.findall(r"(Fixes|Closes|Fixed|close):? #([0-9]+)",
|
|
item['body'], re.MULTILINE)
|
|
for mm in match:
|
|
bugs.add(mm[1])
|
|
|
|
# now open commits
|
|
if args.commits_file:
|
|
print("Open commits file and parse for fixed bugs...")
|
|
with open(args.commits_file, "r") as commits:
|
|
content = commits.read()
|
|
match = re.findall(r"(Fixes|Closes|Fixed|close):? #([0-9]+)",
|
|
str(content), re.MULTILINE)
|
|
for mm in match:
|
|
bugs.add(mm[1])
|
|
|
|
print("Create output file...")
|
|
with Spinner():
|
|
with open(args.file, "w") as f:
|
|
for m in sorted(bugs):
|
|
item = i.get_issue(m)
|
|
if item:
|
|
# * :github:`8193` - STM32 config BUILD_OUTPUT_HEX fail
|
|
f.write("* :github:`{}` - {}\n".format(
|
|
item['number'], item['title'].strip()))
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|