|
|
@ -0,0 +1,188 @@ |
|
|
|
#!/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() |