blob: 1582b071b039f6007064725ae23fb7f7e2729e7e [file] [log] [blame]
# -*- coding: utf-8 -*-
# Copyright 2018 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""cros buildresult: Look up results for a single build."""
from __future__ import print_function
import json
import os
from chromite.cli import command
from chromite.lib import commandline
from chromite.cli.cros import cros_cidbcreds
from chromite.lib import cros_logging as logging
def FetchBuildStatus(db, buildbucket_id=None, cidb_id=None):
"""Fetch the build_status dict for a given build.
Args:
db: CIDBConnection.
buildbucket_id: A buildbucket_id as a str.
cidb_id: A cidb_id as a str.
"""
# Look up build details in CIDB.
if buildbucket_id:
build_status = db.GetBuildStatusWithBuildbucketId(buildbucket_id)
elif cidb_id:
build_status = db.GetBuildStatus(cidb_id)
else:
raise ValueError('Must set buildbucket_id or cidb_id.')
# Exit if the build isn't finished yet.
FINISHED_STATUSES = ('fail', 'pass', 'missing', 'aborted', 'skipped',
'forgiven')
if build_status['status'] not in FINISHED_STATUSES:
logging.error('Build not finished. Status: %s', build_status['status'])
raise SystemExit(2)
# We don't actually store the artifacts_url, but we store a URL for a specific
# artifact we can use to derive it.
build_status['artifacts_url'] = None
if build_status['metadata_url']:
build_status['artifacts_url'] = os.path.dirname(
build_status['metadata_url'])
# Find stage information.
build_status['stages'] = db.GetBuildStages(build_status['id'])
return build_status
def Report(build_statuses):
"""Generate the stdout description of a given build.
Args:
build_statuses: List of build_status dict's from FetchBuildStatus.
Returns:
str to display as the final report.
"""
result = ''
for build_status in build_statuses:
result += '\n'.join([
'cidb_id: %s' % build_status['id'],
'buildbucket_id: %s' % build_status['buildbucket_id'],
'status: %s' % build_status['status'],
'artifacts_url: %s' % build_status['artifacts_url'],
'toolchain_url: %s' % build_status['toolchain_url'],
'stages:\n'
])
for stage in build_status['stages']:
result += ' %s: %s\n' % (stage['name'], stage['status'])
result += '\n' # Blank line between builds.
return result
def ReportJson(build_statuses):
"""Generate the json description of a given build.
Args:
build_statuses: List of build_status dict's from FetchBuildStatus.
Returns:
str to display as the final report.
"""
report = {}
for build_status in build_statuses:
report[build_status['buildbucket_id']] = {
'cidb_id': build_status['id'],
'buildbucket_id': build_status['buildbucket_id'],
'status': build_status['status'],
'stages': {s['name']: s['status'] for s in build_status['stages']},
'artifacts_url': build_status['artifacts_url'],
'toolchain_url': build_status['toolchain_url'],
}
return json.dumps(report)
@command.CommandDecorator('buildresult')
class BuildResultCommand(command.CliCommand):
"""Script that shows build timing for a build, and it's stages."""
def __init__(self, options):
super(BuildResultCommand, self).__init__(options)
@classmethod
def AddParser(cls, parser):
super(cls, BuildResultCommand).AddParser(parser)
# CIDB access credentials.
creds_args = parser.add_argument_group()
creds_args.add_argument('--cred-dir', type='path',
metavar='CIDB_CREDENTIALS_DIR',
help='Database credentials directory with'
' certificates and other connection'
' information. Obtain your credentials'
' at go/cros-cidb-admin .')
creds_args.add_argument('--update-cidb-creds', dest='force_update',
action='store_true',
help='force updating the cidb credentials.')
# What build do we report on?
request_group = parser.add_mutually_exclusive_group()
request_group.add_argument(
'--buildbucket-id', help='Buildbucket ID of build to look up.')
request_group.add_argument(
'--cidb-id', help='CIDB ID of the build to look up.')
# What kind of report do we generate?
parser.add_argument('--report', default='standard',
choices=['standard', 'json'],
help='What format is the output in?')
def Run(self):
"""Run cros buildresult."""
self.options.Freeze()
commandline.RunInsideChroot(self)
credentials = self.options.cred_dir
if not credentials:
credentials = cros_cidbcreds.CheckAndGetCIDBCreds(
force_update=self.options.force_update)
# Delay import so sqlalchemy isn't pulled in until we need it.
from chromite.lib import cidb
db = cidb.CIDBConnection(credentials)
# Turn the single build into a list, so we can easily add new fetch
# methods later that query multiple builds.
build_statuses = [FetchBuildStatus(
db, self.options.buildbucket_id, self.options.cidb_id)]
if self.options.report == 'json':
report = ReportJson(build_statuses)
else:
report = Report(build_statuses)
print(report)