blob: c58920cf2c6e008523bf1eeb5e8848df29a036bc [file] [log] [blame]
#!/usr/bin/env python
# The MIT License (MIT)
#
# Copyright (c) 2015 Jared Morrow (github.com/jaredmorrow)
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
## 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()