blob: 50a50bf882e0dcf061b0acdb8e58fdd0f7f2031e [file] [log] [blame]
#!/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.
### view commits not yet rolled to a layer
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
LAYERS = [
'vendor', # For checkouts building on top of Topaz.
'topaz',
'peridot',
'garnet',
'zircon',
]
def http_get(url):
"""Fetches the content at a given URL."""
try:
target = urllib2.urlopen(url)
return target.read()
finally:
target.close()
def get_lower_layer(layer):
"""Returns the layer immediately below the given layer."""
index = LAYERS.index(layer)
if index == len(LAYERS) - 1:
return None
return LAYERS[index + 1]
def get_lower_layer_commit(layer, at):
"""Returns the pinned revision of the layer below the given layer."""
lower_layer = get_lower_layer(layer)
if not lower_layer:
return (None, None)
url = ('https://fuchsia.googlesource.com/%s/+/%s/manifest/%s?format=TEXT'
% (layer, at, layer))
content = http_get(url)
content = base64.b64decode(content)
manifest = xml.fromstring(content)
nodes = manifest.findall('./imports/import[@name="%s"]' % lower_layer)
return (lower_layer, nodes[0].get('revision'))
def get_commits(layer, revision):
"""Returns the commits in the given layer up to a given commit."""
url = 'https://fuchsia.googlesource.com/%s/+log/master?format=JSON' % layer
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)
for commit in data['log']:
if commit['commit'] == revision:
return
result.append(commit)
get_more(result, start=content['next'])
result = []
get_more(result)
return result
def filter_commit(commit):
"""Returns True if a commit should be listed."""
author = commit['author']['name']
if author == 'skia-fuchsia-autoroll@skia-buildbots.google.com.iam.gserviceaccount.com':
return False
for layer in LAYERS:
if author == '%s-roller' % layer:
return False
return True
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(layer, commits):
"""Prints the given commits in a user=pleasing format."""
commits = [c for c in commits if filter_commit(c)]
if commits:
timestamp = commits[-1]['committer']['time']
elapsed_time = get_time_since(timestamp)
else:
elapsed_time = ''
print('--------------')
print('| %s | %s' % ('{:^10}'.format(layer), elapsed_time))
print('--------------')
for commit in commits:
print('%s | %s | %s' % (commit['commit'][:7],
commit['author']['name'][:15].ljust(15),
commit['message'].splitlines()[0]))
if not commits:
print('None')
def main():
parser = argparse.ArgumentParser(formatter_class=RawTextHelpFormatter,
description="""Displays the commits not yet rolled to a given layer.
For vendor layers, specify the Topaz revision with --topaz-revision.""")
parser.add_argument('--layer',
help='Name of the layer to inspect',
choices=LAYERS,
default='topaz')
parser.add_argument('--topaz-revision',
help='(vendor only) revision of the Topaz layer')
args = parser.parse_args()
layer = args.layer
revision = args.topaz_revision
if layer != 'vendor' and revision:
print('Warning: Topaz revision only used with vendor layers.')
if layer == 'vendor' and not revision:
print('Error: vendor layers require a Topaz revision.')
return 1
print('Pending commits for: %s' % layer.upper())
if layer == 'vendor':
commits = get_commits('topaz', revision)
print_commits('topaz', commits)
layer = 'topaz'
else:
revision = 'master'
while True:
(lower_layer, lower_revision) = get_lower_layer_commit(layer, revision)
if not lower_layer:
break
commits = get_commits(lower_layer, lower_revision)
print_commits(lower_layer, commits)
layer = lower_layer
revision = lower_revision
return 0
if __name__ == "__main__":
sys.exit(main())