blob: 0bfc92cb0fdd0a233a01ad8fd41135a0fb6322ae [file] [log] [blame] [edit]
#!/usr/bin/env python
# Copyright 2017 The Fuchsia Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
#### CATEGORY=Source tree
### view commits not yet published to global integration
import argparse
from argparse import RawTextHelpFormatter
import base64
from datetime import datetime, timedelta, tzinfo
import json
import sys
import urllib2
import xml.etree.ElementTree as xml
PETALS = [
('experiences', 'https://fuchsia.googlesource.com/integration/+/HEAD/flower?format=TEXT'),
('topaz', 'https://fuchsia.googlesource.com/integration/+/HEAD/topaz/minimal?format=TEXT'),
('fuchsia', 'https://fuchsia.googlesource.com/integration/+/HEAD/stem?format=TEXT'),
]
# Authors whose commits are not displayed.
IGNORED_AUTHORS = [
'skia-fuchsia-autoroll@skia-buildbots.google.com.iam.gserviceaccount.com',
'third-party-roller',
'topaz-roller',
'peridot-roller',
'garnet-roller',
'zircon-roller',
]
def http_get(url):
"""Fetches the content at a given URL."""
try:
target = urllib2.urlopen(url)
return target.read()
finally:
if target:
target.close()
def get_published_commit_for(petal):
"""Returns the pinned revision of a petal in global integration."""
name = petal[0]
url = petal[1]
content = http_get(url)
content = base64.b64decode(content)
manifest = xml.fromstring(content)
nodes = manifest.findall('./projects/project[@name="%s"]' % name)
return (name, nodes[0].get('revision'))
def get_published_commits():
"""Returns the published revision of all the petals."""
return [get_published_commit_for(petal) for petal in PETALS]
def get_commits(petal, revision):
"""Returns the commits in the given petal up to a given commit."""
url = 'https://fuchsia.googlesource.com/%s/+log/master?format=JSON' % petal
def get_more(result, start=None):
get_url = url
if start:
get_url = '%s&s=%s' % (url, start)
content = http_get(get_url)
# Remove the anti-XSSI header.
content = content[5:]
data = json.loads(content.decode('utf-8', errors='replace'))
for commit in data['log']:
if commit['commit'] == revision:
return
result.append(commit)
get_more(result, start=data['next'])
result = []
get_more(result)
return result
def filter_commit(commit):
"""Returns True if a commit should be listed."""
return commit['author']['name'] not in IGNORED_AUTHORS
class MyTimezone(tzinfo):
"""Simple timezone implementation, since for some reason Python 2.7 doesn't
provide one.
"""
def __init__(self, data=None):
self.data = data if data else '+0000'
def utcoffset(self, dt):
hours = int(self.data[1:3])
minutes = int(self.data[3:5])
delta = timedelta(hours=hours, minutes=minutes)
if self.data[0] == '-':
delta = -delta
return delta
def tzname(self, dt):
return 'Bogus'
def dst(self, dt):
return timedelta(0)
def get_time_since(timestamp):
"""Returns a string describing the amount of time elapsed since the given
timestamp.
Timestamp format: Sat Feb 10 03:17:06 2018 +0000
"""
timestamp_no_tz = timestamp[:-6]
date_no_tz = datetime.strptime(timestamp_no_tz, '%a %b %d %H:%M:%S %Y')
date = date_no_tz.replace(tzinfo=MyTimezone(timestamp[-5:]))
now = datetime.utcnow().replace(tzinfo=MyTimezone())
delta = now - date
if delta.days >= 1:
return '>1d'
hours = delta.seconds / 3600
if hours >= 1:
return '%sh' % hours
minutes = (delta.seconds % 3600) / 60
return '%sm' % minutes
def print_commits(petal, commits, print_all=False):
"""Prints the given commits in a user=pleasing format."""
commit_filter = (lambda c: c) if print_all else filter_commit
commits = filter(commit_filter, commits)
if commits:
timestamp = commits[-1]['committer']['time']
elapsed_time = get_time_since(timestamp)
else:
elapsed_time = ''
print('--------------')
print('| %s | %s | %s commits' % ('{:^10}'.format(petal), elapsed_time, len(commits)))
print('--------------')
for commit in commits:
line = u'%s | %s | %s' % (commit['commit'][:7],
commit['author']['name'][:15].ljust(15),
commit['message'].splitlines()[0])
print(line.encode('utf-8', errors='replace'))
if not commits:
print('None')
def main():
parser = argparse.ArgumentParser(formatter_class=RawTextHelpFormatter,
description="""Displays the commits not yet published to global integration.""")
parser.add_argument('--all',
help='Whether to print all commits, including rollers',
action='store_true')
args = parser.parse_args()
for (petal, published_commit) in get_published_commits():
commits = get_commits(petal, published_commit)
print_commits(petal, commits, print_all=args.all)
return 0
if __name__ == "__main__":
sys.exit(main())