You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

188 line
5.5 KiB

9 年之前
  1. #!/usr/bin/env python
  2. ## Install info
  3. ## $ virtualenv env
  4. ## $ source env/bin/activate
  5. ## $ pip install PyGithub
  6. ##
  7. ## Examples:
  8. ## Find the differences from last tag to current
  9. ## $ pr2relnotes.py alpha-6 HEAD
  10. import argparse
  11. import re
  12. import os
  13. import subprocess
  14. from github import Github
  15. from github import GithubException
  16. def dprint(*args):
  17. if VERBOSE:
  18. print str(args)
  19. def get_args():
  20. """
  21. Get command line arguments
  22. """
  23. parser = argparse.ArgumentParser(description="Find the PR's between two versions")
  24. parser.add_argument("old", help = "old version to use")
  25. parser.add_argument("new", help = "new version to use")
  26. parser.add_argument("-v", "--verbose", help="Enable debug output",
  27. default=False,
  28. action="store_true")
  29. parser.add_argument("-f", "--file",
  30. help="Output file to store results (default: tagdiff.md)",
  31. default="tagdiff.md")
  32. return parser.parse_args()
  33. def search_prs(log):
  34. """
  35. Search lines of text for PR numbers
  36. """
  37. # Find all matches using regex iterator, using the PR # as the group match
  38. resultlist = [str(m.group(1)) for m in re.finditer(r"erge pull request #(\d+)", log)]
  39. return sorted(resultlist)
  40. def get_env(env):
  41. return os.environ[env]
  42. def get_formatted_issue(repo, issue, title, url):
  43. """
  44. Single place to adjust formatting output of PR data
  45. """
  46. # Newline support writelines() call which doesn't add newlines
  47. # on its own
  48. return("* {}/{}: [{}]({})\n".format(repo, issue, title.encode('utf-8'), url))
  49. def gh_get_issue_output(org, repo, issuenum):
  50. """
  51. Look up PR information using the GitHub api
  52. """
  53. # Attempt to look up the PR, and don't take down the whole
  54. # shebang if a API call fails
  55. # This will fail often on forks who don't have the
  56. # PRs numbers associated with the forked account
  57. # Return empty string on error
  58. try:
  59. repoObj = gh.get_repo(org + "/" + repo)
  60. issue = repoObj.get_issue(int(issuenum))
  61. title = issue.title
  62. html_url = issue.html_url
  63. except GithubException as e:
  64. print "Github error({0}): {1}".format(e.status, e.data)
  65. return ""
  66. except:
  67. print "Some github error"
  68. return ""
  69. return(get_formatted_issue(repo, issuenum, title, html_url))
  70. def get_org(repourl):
  71. """
  72. Simple function to parse the organization out of a GitHub URL
  73. """
  74. dprint("Current repourl to search: " + repourl)
  75. # GitHub URLs can be:
  76. # http[s]://www.github.com/org/repo
  77. # or git@github.com:/org/repo
  78. pattern = re.compile(r"github.com[/:]+(\w+)/")
  79. m = re.search(pattern, repourl)
  80. # Fail fast if this is wrong so we can add a pattern to the search
  81. if m:
  82. return m.group(1)
  83. else:
  84. raise Exception("Incorrect regex pattern finding repo org")
  85. def get_name(repourl):
  86. """
  87. Simple function to parse the repository name out of a GitHub URL
  88. """
  89. dprint("Current repourl to search: " + repourl)
  90. repo_pattern = re.compile(r"github.com[/:]\w+/(\w+)")
  91. m = re.search(repo_pattern, repourl)
  92. if m:
  93. return m.group(1)
  94. else:
  95. raise Exception("Incorrect rexex pattern finding repo url")
  96. def get_repo_url_from_remote():
  97. """
  98. Function that gets the repository URL from the `git remote` listing
  99. """
  100. git_remote_bytes = subprocess.check_output(["git", "remote", "-v"])
  101. # check_output returns the command results in raw byte format
  102. remote_string = git_remote_bytes.decode('utf-8')
  103. pattern = re.compile(r"github.com[/:]\w+/\w+")
  104. m = re.search(pattern, remote_string)
  105. if m:
  106. return m.group(0)
  107. else:
  108. raise Exception("Incorrect rexex pattern finding repo url")
  109. def process_log(gitlog, repo_url):
  110. """
  111. Handles the processing of the gitlog and returns a list
  112. of PRs already formatted for output
  113. """
  114. pr_list = search_prs(gitlog)
  115. repoorg = get_org(repo_url)
  116. reponame = get_name(repo_url)
  117. pr_buffer = []
  118. for issue in pr_list:
  119. pr_buffer.append(gh_get_issue_output(repoorg, reponame, issue))
  120. return pr_buffer
  121. def fetch_log(old_ver, new_ver):
  122. """
  123. Function that processes the git log between the old and new versions
  124. """
  125. dprint("Current working directory", os.getcwd())
  126. gitlogbytes = subprocess.check_output(["git", "log",
  127. str(old_ver + ".." + new_ver)])
  128. return gitlogbytes.decode('utf-8')
  129. def compare_versions(repo_url, old_ver, new_ver):
  130. # Formatted list of all PRs for all repos
  131. pr_out = []
  132. gitlog = fetch_log(old_ver, new_ver)
  133. pr_out.extend(process_log(gitlog, repo_url))
  134. return pr_out
  135. def main():
  136. args = get_args()
  137. # Setup the GitHub object for later use
  138. global gh
  139. gh = Github(get_env("GHAUTH"))
  140. if gh == "":
  141. raise Exception("Env var GHAUTH must be set to a valid GitHub API key")
  142. if args.verbose:
  143. global VERBOSE
  144. VERBOSE=True
  145. dprint("Inspecting difference in between: ", args.old, " and ", args.new)
  146. # Find the github URL of the repo we are operating on
  147. repo_url = get_repo_url_from_remote()
  148. # Compare old and new versions
  149. pr_list = compare_versions(repo_url, args.old, args.new)
  150. # Writeout PR listing
  151. print "Writing output to file %s" % args.file
  152. with open(args.file, 'w') as output:
  153. output.writelines(pr_list)
  154. if __name__ == "__main__":
  155. VERBOSE=False
  156. gh=None
  157. topdir=os.getcwd()
  158. main()