Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(713)

Unified Diff: git_cl.py

Issue 2274743003: Add a --json option to git cl try-results. (Closed) Base URL: https://chromium.googlesource.com/chromium/tools/depot_tools.git@master
Patch Set: WIP: Make --json option output the same info as human-readable option; refactor Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | tests/git_cl_test.py » ('j') | tests/git_cl_test.py » ('J')
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: git_cl.py
diff --git a/git_cl.py b/git_cl.py
index 5831b121ee638d0fe779203991b1692bd60b8db3..1bf4601f7666f95f7de2c8171e34dc4172f6b1ba 100755
--- a/git_cl.py
+++ b/git_cl.py
@@ -398,7 +398,7 @@ def trigger_try_jobs(auth_config, changelist, options, masters, category):
def fetch_try_jobs(auth_config, changelist, options):
"""Fetches try jobs from buildbucket.
- Returns a map from build id to build info as json dictionary.
+ Returns a map from build id to build info as a dictionary.
"""
rietveld_url = settings.GetDefaultServerUrl()
rietveld_host = urlparse.urlparse(rietveld_url).hostname
@@ -434,17 +434,24 @@ def fetch_try_jobs(auth_config, changelist, options):
return builds
-def print_try_jobs(options, builds):
- """Prints nicely result of fetch_try_jobs."""
- if not builds:
- print('No try jobs scheduled')
- return
+def try_job_buckets(builds):
+ """Groups a set of try jobs into buckets for output.
+
+ Args:
+ builds: A list of dicts, one for each try job. This is the output
+ of fetch_try_jobs.
+ Returns:
+ A dict mapping categories of try jobs (e.g. successes, failures,
+ infra failures, etc.) to lists of dicts, one for each try job.
+ The information here is a subset of the information in the original
+ input build dicts.
+ """
# Make a copy, because we'll be modifying builds dictionary.
builds = builds.copy()
builder_names_cache = {}
- def get_builder(b):
+ def get_builder_name(b):
try:
return builder_names_cache[b['id']]
except KeyError:
@@ -452,85 +459,128 @@ def print_try_jobs(options, builds):
parameters = json.loads(b['parameters_json'])
name = parameters['builder_name']
except (ValueError, KeyError) as error:
- print('WARNING: failed to get builder name for build %s: %s' % (
- b['id'], error))
+ print('WARNING: failed to get builder name for build %s: %s' %
+ (b['id'], error))
name = None
builder_names_cache[b['id']] = name
return name
- def get_bucket(b):
+ def get_master_name(b):
bucket = b['bucket']
if bucket.startswith('master.'):
return bucket[len('master.'):]
return bucket
- if options.print_master:
- name_fmt = '%%-%ds %%-%ds' % (
- max(len(str(get_bucket(b))) for b in builds.itervalues()),
- max(len(str(get_builder(b))) for b in builds.itervalues()))
- def get_name(b):
- return name_fmt % (get_bucket(b), get_builder(b))
- else:
- name_fmt = '%%-%ds' % (
- max(len(str(get_builder(b))) for b in builds.itervalues()))
- def get_name(b):
- return name_fmt % get_builder(b)
-
def sort_key(b):
- return b['status'], b.get('result'), get_name(b), b.get('url')
+ return b['status'], b.get('result'), get_builder_name(b), b.get('url')
- def pop(title, f, color=None, **kwargs):
- """Pop matching builds from `builds` dict and print them."""
-
- if not options.color or color is None:
- colorize = str
- else:
- colorize = lambda x: '%s%s%s' % (color, x, Fore.RESET)
+ buckets = collections.OrderedDict()
+ def pop(title, **kwargs):
+ """Pops matching builds from `builds` and adds it to `buckets`."""
result = []
for b in builds.values():
if all(b.get(k) == v for k, v in kwargs.iteritems()):
builds.pop(b['id'])
result.append(b)
if result:
- print(colorize(title))
+ buckets[title] = []
for b in sorted(result, key=sort_key):
- print(' ', colorize('\t'.join(map(str, f(b)))))
-
- total = len(builds)
- pop(status='COMPLETED', result='SUCCESS',
- title='Successes:', color=Fore.GREEN,
- f=lambda b: (get_name(b), b.get('url')))
- pop(status='COMPLETED', result='FAILURE', failure_reason='INFRA_FAILURE',
- title='Infra Failures:', color=Fore.MAGENTA,
- f=lambda b: (get_name(b), b.get('url')))
- pop(status='COMPLETED', result='FAILURE', failure_reason='BUILD_FAILURE',
- title='Failures:', color=Fore.RED,
- f=lambda b: (get_name(b), b.get('url')))
- pop(status='COMPLETED', result='CANCELED',
- title='Canceled:', color=Fore.MAGENTA,
- f=lambda b: (get_name(b),))
- pop(status='COMPLETED', result='FAILURE',
- failure_reason='INVALID_BUILD_DEFINITION',
- title='Wrong master/builder name:', color=Fore.MAGENTA,
- f=lambda b: (get_name(b),))
- pop(status='COMPLETED', result='FAILURE',
- title='Other failures:',
- f=lambda b: (get_name(b), b.get('failure_reason'), b.get('url')))
- pop(status='COMPLETED',
- title='Other finished:',
- f=lambda b: (get_name(b), b.get('result'), b.get('url')))
- pop(status='STARTED',
- title='Started:', color=Fore.YELLOW,
- f=lambda b: (get_name(b), b.get('url')))
- pop(status='SCHEDULED',
- title='Scheduled:',
- f=lambda b: (get_name(b), 'id=%s' % b['id']))
+ buckets[title].append({
+ 'builder_name': get_builder_name(b),
+ 'master_name': get_master_name(b),
+ 'url': b.get('url'),
+ 'buildbucket_id': b.get('id'),
+ 'status': b.get('status'),
+ 'result': b.get('result'),
+ 'failure_reason': b.get('failure_reason'),
+ })
+
+ pop('Successes', status='COMPLETED', result='SUCCESS')
+ pop('Infra Failures', status='COMPLETED', result='FAILURE',
+ failure_reason='INFRA_FAILURE')
+ pop('Failures', status='COMPLETED', result='FAILURE',
+ failure_reason='BUILD_FAILURE')
+ pop('Canceled', status='COMPLETED', result='CANCELED')
+ pop('Wrong master/builder name', status='COMPLETED', result='FAILURE',
+ failure_reason='INVALID_BUILD_DEFINITION')
+ pop('Other failures', status='COMPLETED', result='FAILURE')
+ pop('Other finished', status='COMPLETED')
+ pop('Started', status='STARTED')
+ pop('Scheduled', status='SCHEDULED')
# The last section is just in case buildbucket API changes OR there is a bug.
- pop(title='Other:',
- f=lambda b: (get_name(b), 'id=%s' % b['id']))
+ pop('Other')
assert len(builds) == 0
- print('Total: %d try jobs' % total)
+ return buckets
+
+
+def print_try_jobs(options, builds):
+ """Nicely prints the result of fetch_try_jobs."""
+ if not builds:
+ print('No try jobs scheduled')
+ return
+
+ buckets = try_job_buckets(builds)
+
+ def color_for_bucket(title):
+ return {
+ 'Successes': Fore.GREEN,
+ 'Infra Failures': Fore.MAGENTA,
+ 'Failures': Fore.RED,
+ 'Canceled': Fore.MAGENTA,
+ 'Wrong master/builder name': Fore.MAGENTA,
+ 'Started:': Fore.YELLOW,
+ }.get(title)
+
+ def extra_columns_for_bucket(title):
+ return {
+ 'Successes': ['url'],
+ 'Infra Failures': ['url'],
+ 'Failures': ['url'],
+ 'Canceled': [],
+ 'Wrong master/builder name': ['builder_name'],
+ 'Other failures': ['failure_reason', 'url'],
+ 'Other finished': ['result', 'url'],
+ 'Started': ['url'],
+ 'Scheduled': ['buildbucket_id'],
+ 'Other': ['buildbucket_id'],
+ }.get(title)
+
+ for title, builds in buckets.iteritems():
+ if not builds:
+ continue
+ color = color_for_bucket(title)
+ if not options.color or color is None:
+ colorize = str
+ else:
+ colorize = lambda x: '%s%s%s' % (color, x, Fore.RESET)
+ print(colorize(title + ':'))
+ for build in builds:
+ row = [build['builder_name']]
+ # TODO: Make sure the spacing is right when master name is printed.
+ if options.print_master:
+ row.append([build['master_name']])
+ for column in extra_columns_for_bucket(title):
+ if column == 'id':
+ row.append('id=%s' % build['id'])
+ else:
+ row.append(build[column])
+ print(' ', colorize('\t'.join(row)))
+ print('Total: %d try jobs' % sum(len(b) for b in buckets))
+
+
+def write_try_results_json(output_file, builds):
+ """This outputs results from fetch_try_jobs as JSON.
+
+ Args:
+ builds: Results fetched from fetch_try_jobs. This is a list of dicts.
+
+ Returns:
+ A dict mapping "status buckets" to lists of builds.
+ """
+ write_json(output_file + '.full', builds)
+ buckets = try_job_buckets(builds)
+ write_json(output_file, buckets)
def MatchSvnGlob(url, base_url, glob_spec, allow_wildcards):
@@ -4757,6 +4807,8 @@ def CMDtry_results(parser, args):
group.add_option(
"--buildbucket-host", default='cr-buildbucket.appspot.com',
help="Host of buildbucket. The default host is %default.")
+ group.add_option(
+ '--json', help='Path of JSON output file to write try job results to.')
parser.add_option_group(group)
auth.add_auth_options(parser)
options, args = parser.parse_args(args)
@@ -4785,7 +4837,10 @@ def CMDtry_results(parser, args):
print('ERROR: Exception when trying to fetch try jobs: %s\n%s' %
(e, stacktrace))
return 1
- print_try_jobs(options, jobs)
+ if options.json:
+ write_try_results_json(options.json, jobs)
+ else:
+ print_try_jobs(options, jobs)
return 0
« no previous file with comments | « no previous file | tests/git_cl_test.py » ('j') | tests/git_cl_test.py » ('J')

Powered by Google App Engine
This is Rietveld 408576698