Skip to content

Instantly share code, notes, and snippets.

@t20100
Last active May 13, 2016 08:14
Show Gist options
  • Save t20100/dd7c4bb0b87994532f413ae91fe19501 to your computer and use it in GitHub Desktop.
Save t20100/dd7c4bb0b87994532f413ae91fe19501 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python3
# coding: utf-8
# /*##########################################################################
#
# Copyright (c) 2016 European Synchrotron Radiation Facility
#
# 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.
#
# ###########################################################################*/
"""
Get list of Pull requests since given tag.
Also provides a set of functions to get information about a github repository
through github API.
"""
import json
import logging
logging.basicConfig()
_logger = logging.getLogger(__name__)
_logger.setLevel(logging.WARNING)
try:
import urllib.request
except ImportError:
_logger.error('Python 3 only')
raise
def get_github_api(request):
"""Get info from github API
:param str request: The HTTP GET request path.
:return: Response converted from json to Python.
"""
if not request.startswith('https://api.github.com'):
assert request.startswith('/')
request = 'https://api.github.com' + request
_logger.info('Getting url: %s', request)
response = urllib.request.urlopen(request)
_logger.debug(
'Response header %s',
'\n'.join('\t%s: %s' % (f, v) for f, v in response.getheaders()))
payload = response.read().decode('utf-8')
return json.loads(payload)
def get_all_pages(request, max_pages=100):
"""Get info from github API by agregating all pages if any.
:param str request: The HTTP GET request path.
:param int max_pages: The max number of pages to download.
:return: Concatenated response lists converted from json to Python.
"""
results = []
for page in range(1, max_pages):
sep = '?' if '?' not in request else '&'
result = get_github_api(request + sep + 'page=%d' % page)
if not result:
break
results.extend(result)
return results
def get_pull_requests(owner, repo, state='all', base='master'):
"""Get Pull Requests from a github repository.
See https://developer.github.com/v3/pulls/#list-pull-requests
:param str owner: The github user or organisation.
:param str repo: The name of the repository.
:param str state: state of PRs to get, either 'all', 'open' or 'closed'.
:param str base: Base branch of the Pull Requests
:return: List of Pull Requests
"""
assert state in ('all', 'open', 'closed')
PR_URL_TEMPLATE = \
'/repos/{owner}/{repo}/pulls?state={state}&base={base}'
return get_all_pages(PR_URL_TEMPLATE.format(
owner=owner, repo=repo, state=state, base=base))
def sort_pull_requests_by_merged_at(pull_requests):
"""Sort a list of pull requests by date of merge
:param pull_requests: List of pull requests
:return: List of pull requests sorted by merged date
"""
return sorted(pull_requests, key=lambda pr: pr['merged_at'])
def get_info(info, owner, repo):
"""Get releases or tags of a github repository.
For releases, see https://developer.github.com/v3/repos/releases/#list-releases-for-a-repository.
For tags, see https://developer.github.com/v3/repos/#list-tags.
:param str info: Type of information to get 'releases', 'tags'
:param str owner: The github user or organisation.
:param str repo: The name of the repository.
""" # noqa
assert info in ('releases', 'tags')
return get_github_api('/repos/{owner}/{repo}/{info}'.format(
owner=owner, repo=repo, info=info))
if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser(
description='Get github repo Pull Requests merged since a given tag')
parser.add_argument('owner', help='github user or organisation')
parser.add_argument('repo', help='github repository name')
parser.add_argument('tag', help='The tag since which to get Pull Requests')
args = parser.parse_args()
# Retrieve commit date corresponding to selected tag
tags = get_info('tags', args.owner, args.repo)
selected_tags = [tag for tag in tags if tag['name'] == args.tag]
if len(selected_tags) != 1:
raise RuntimeError('No tag %s on repository %s/%s' % (
args.tag, args.owner, args.repo))
tag = selected_tags[0]
commit = get_github_api(tag['commit']['url'])
tag_date = commit['commit']['committer']['date']
# Retrieve all closed PRs
closed_prs = get_pull_requests(args.owner, args.repo, 'closed')
selected_prs = [pr for pr in closed_prs if pr['merged_at'] > tag_date]
selected_prs = sort_pull_requests_by_merged_at(selected_prs)
print('Repository %s/%s' % (args.owner, args.repo))
print('Pull Requests since tag %s (%s):' % (args.tag, tag_date))
print('')
print('----------')
for pr in selected_prs:
print('#%d: %s' % (pr['number'], pr['title']))
print('\tMerged %s' % pr['merged_at'])
print('')
print(pr['body'])
print('----------')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment