#!/usr/bin/env python
|
|
|
|
## Install info
|
|
## $ virtualenv env
|
|
## $ source env/bin/activate
|
|
## $ pip install PyGithub
|
|
##
|
|
## Examples:
|
|
## Find the differences from last tag to current
|
|
## $ pr2relnotes.py alpha-6 HEAD
|
|
|
|
import argparse
|
|
import re
|
|
import os
|
|
import subprocess
|
|
from github import Github
|
|
from github import GithubException
|
|
|
|
|
|
def dprint(*args):
|
|
if VERBOSE:
|
|
print str(args)
|
|
|
|
def get_args():
|
|
"""
|
|
Get command line arguments
|
|
"""
|
|
parser = argparse.ArgumentParser(description="Find the PR's between two versions")
|
|
parser.add_argument("old", help = "old version to use")
|
|
parser.add_argument("new", help = "new version to use")
|
|
parser.add_argument("-v", "--verbose", help="Enable debug output",
|
|
default=False,
|
|
action="store_true")
|
|
parser.add_argument("-f", "--file",
|
|
help="Output file to store results (default: tagdiff.md)",
|
|
default="tagdiff.md")
|
|
return parser.parse_args()
|
|
|
|
def search_prs(log):
|
|
"""
|
|
Search lines of text for PR numbers
|
|
"""
|
|
# Find all matches using regex iterator, using the PR # as the group match
|
|
resultlist = [str(m.group(1)) for m in re.finditer(r"erge pull request #(\d+)", log)]
|
|
return sorted(resultlist)
|
|
|
|
def get_env(env):
|
|
return os.environ[env]
|
|
|
|
def get_formatted_issue(repo, issue, title, url):
|
|
"""
|
|
Single place to adjust formatting output of PR data
|
|
"""
|
|
# Newline support writelines() call which doesn't add newlines
|
|
# on its own
|
|
return("* {}/{}: [{}]({})\n".format(repo, issue, title, url))
|
|
|
|
def gh_get_issue_output(org, repo, issuenum):
|
|
"""
|
|
Look up PR information using the GitHub api
|
|
"""
|
|
# Attempt to look up the PR, and don't take down the whole
|
|
# shebang if a API call fails
|
|
# This will fail often on forks who don't have the
|
|
# PRs numbers associated with the forked account
|
|
# Return empty string on error
|
|
try:
|
|
repoObj = gh.get_repo(org + "/" + repo)
|
|
issue = repoObj.get_issue(int(issuenum))
|
|
title = issue.title
|
|
html_url = issue.html_url
|
|
except GithubException as e:
|
|
print "Github error({0}): {1}".format(e.status, e.data)
|
|
return ""
|
|
except:
|
|
print "Some github error"
|
|
return ""
|
|
|
|
return(get_formatted_issue(repo, issuenum, title, html_url))
|
|
|
|
|
|
def get_org(repourl):
|
|
"""
|
|
Simple function to parse the organization out of a GitHub URL
|
|
"""
|
|
dprint("Current repourl to search: " + repourl)
|
|
# GitHub URLs can be:
|
|
# http[s]://www.github.com/org/repo
|
|
# or git@github.com:/org/repo
|
|
pattern = re.compile(r"github.com[/:]+(\w+)/")
|
|
m = re.search(pattern, repourl)
|
|
# Fail fast if this is wrong so we can add a pattern to the search
|
|
if m:
|
|
return m.group(1)
|
|
else:
|
|
raise Exception("Incorrect regex pattern finding repo org")
|
|
|
|
def get_name(repourl):
|
|
"""
|
|
Simple function to parse the repository name out of a GitHub URL
|
|
"""
|
|
dprint("Current repourl to search: " + repourl)
|
|
repo_pattern = re.compile(r"github.com[/:]\w+/(\w+)")
|
|
m = re.search(repo_pattern, repourl)
|
|
if m:
|
|
return m.group(1)
|
|
else:
|
|
raise Exception("Incorrect rexex pattern finding repo url")
|
|
|
|
def get_repo_url_from_remote():
|
|
"""
|
|
Function that gets the repository URL from the `git remote` listing
|
|
"""
|
|
git_remote_bytes = subprocess.check_output(["git", "remote", "-v"])
|
|
# check_output returns the command results in raw byte format
|
|
remote_string = git_remote_bytes.decode('utf-8')
|
|
|
|
pattern = re.compile(r"github.com[/:]\w+/\w+")
|
|
m = re.search(pattern, remote_string)
|
|
if m:
|
|
return m.group(0)
|
|
else:
|
|
raise Exception("Incorrect rexex pattern finding repo url")
|
|
|
|
def process_log(gitlog, repo_url):
|
|
"""
|
|
Handles the processing of the gitlog and returns a list
|
|
of PRs already formatted for output
|
|
"""
|
|
pr_list = search_prs(gitlog)
|
|
repoorg = get_org(repo_url)
|
|
reponame = get_name(repo_url)
|
|
pr_buffer = []
|
|
for issue in pr_list:
|
|
pr_buffer.append(gh_get_issue_output(repoorg, reponame, issue))
|
|
|
|
return pr_buffer
|
|
|
|
def fetch_log(old_ver, new_ver):
|
|
"""
|
|
Function that processes the git log between the old and new versions
|
|
"""
|
|
dprint("Current working directory", os.getcwd())
|
|
gitlogbytes = subprocess.check_output(["git", "log",
|
|
str(old_ver + ".." + new_ver)])
|
|
return gitlogbytes.decode('utf-8')
|
|
|
|
|
|
def compare_versions(repo_url, old_ver, new_ver):
|
|
# Formatted list of all PRs for all repos
|
|
pr_out = []
|
|
gitlog = fetch_log(old_ver, new_ver)
|
|
pr_out.extend(process_log(gitlog, repo_url))
|
|
return pr_out
|
|
|
|
def main():
|
|
args = get_args()
|
|
|
|
# Setup the GitHub object for later use
|
|
global gh
|
|
gh = Github(get_env("GHAUTH"))
|
|
|
|
if gh == "":
|
|
raise Exception("Env var GHAUTH must be set to a valid GitHub API key")
|
|
|
|
if args.verbose:
|
|
global VERBOSE
|
|
VERBOSE=True
|
|
|
|
dprint("Inspecting difference in between: ", args.old, " and ", args.new)
|
|
|
|
# Find the github URL of the repo we are operating on
|
|
repo_url = get_repo_url_from_remote()
|
|
|
|
# Compare old and new versions
|
|
pr_list = compare_versions(repo_url, args.old, args.new)
|
|
|
|
# Writeout PR listing
|
|
print "Writing output to file %s" % args.file
|
|
with open(args.file, 'w') as output:
|
|
output.writelines(pr_list)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
VERBOSE=False
|
|
gh=None
|
|
topdir=os.getcwd()
|
|
main()
|