| OLD | NEW |
| 1 # Copyright 2016 The Chromium Authors. All rights reserved. | 1 # Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 | 4 |
| 5 """Utility functions to communicate with Rietveld.""" | 5 """Utility functions to communicate with Rietveld.""" |
| 6 | 6 |
| 7 import json | 7 import json |
| 8 import logging | 8 import logging |
| 9 import urllib2 | 9 import urllib2 |
| 10 | 10 |
| 11 from webkitpy.common.net.buildbot import Build | 11 from webkitpy.common.net.buildbot import Build |
| 12 | 12 |
| 13 _log = logging.getLogger(__name__) | 13 _log = logging.getLogger(__name__) |
| 14 | 14 |
| 15 BASE_CODEREVIEW_URL = 'https://codereview.chromium.org/api' | 15 BASE_CODEREVIEW_URL = 'https://codereview.chromium.org/api' |
| 16 | 16 |
| 17 | 17 |
| 18 class Rietveld(object): | 18 class Rietveld(object): |
| 19 | 19 |
| 20 def __init__(self, web): | 20 def __init__(self, web): |
| 21 self.web = web | 21 self.web = web |
| 22 | 22 |
| 23 def latest_try_jobs(self, issue_number, builder_names, patchset_number=None)
: | 23 def latest_try_job_results(self, issue_number, builder_names=None, patchset_
number=None): |
| 24 """Returns a list of Build objects for jobs on the latest patchset. | 24 """Returns a list of Build objects for builds on the latest patchset. |
| 25 | 25 |
| 26 Args: | 26 Args: |
| 27 issue_number: A Rietveld issue number. | 27 issue_number: A Rietveld issue number. |
| 28 builder_names: A collection of builder names to list jobs for. | 28 builder_names: A collection of builder names. If specified, only res
ults |
| 29 patchset_number: Use a specific patchset instead of the latest one. | 29 from the given list of builders will be kept. |
| 30 patchset_number: If given, a specific patchset will be used instead
of the latest one. |
| 30 | 31 |
| 31 Returns: | 32 Returns: |
| 32 A list of Build objects for the latest job for each builder, on the | 33 A dict mapping Build objects to result dicts for the latest build |
| 33 latest patchset. If none were found, an empty list is returned. | 34 for each builder on the latest patchset. |
| 34 """ | 35 """ |
| 35 try: | 36 try: |
| 36 if patchset_number: | 37 if patchset_number: |
| 37 url = self._patchset_url(issue_number, patchset_number) | 38 url = self._patchset_url(issue_number, patchset_number) |
| 38 else: | 39 else: |
| 39 url = self._latest_patchset_url(issue_number) | 40 url = self._latest_patchset_url(issue_number) |
| 40 patchset_data = self._get_json(url) | 41 patchset_data = self._get_json(url) |
| 41 except (urllib2.URLError, ValueError): | 42 except (urllib2.URLError, ValueError): |
| 42 return [] | 43 return {} |
| 43 jobs = [] | 44 |
| 44 for job in patchset_data['try_job_results']: | 45 def build(job): |
| 45 if job['builder'] not in builder_names: | 46 return Build(builder_name=job['builder'], build_number=job['buildnum
ber']) |
| 46 continue | 47 |
| 47 jobs.append(Build( | 48 results = {build(job): job for job in patchset_data['try_job_results']} |
| 48 builder_name=job['builder'], | 49 |
| 49 build_number=job['buildnumber'])) | 50 if builder_names is not None: |
| 50 return self.filter_latest_jobs(jobs) | 51 results = {b: result for b, result in results.iteritems() if b.build
er_name in builder_names} |
| 52 |
| 53 latest_builds = self._filter_latest_builds(list(results)) |
| 54 return {b: result for b, result in results.iteritems() if b in latest_bu
ilds} |
| 55 |
| 56 def _filter_latest_builds(self, builds): |
| 57 """Filters out a collection of Build objects to include only the latest
for each builder. |
| 58 |
| 59 Args: |
| 60 jobs: A list of Build objects. |
| 61 |
| 62 Returns: |
| 63 A list of Build objects that contains only the latest build for each
builder. |
| 64 """ |
| 65 builder_to_highest_number = {} |
| 66 for build in builds: |
| 67 if build.build_number > builder_to_highest_number.get(build.builder_
name, 0): |
| 68 builder_to_highest_number[build.builder_name] = build.build_numb
er |
| 69 |
| 70 def is_latest_build(build): |
| 71 if build.builder_name not in builder_to_highest_number: |
| 72 return False |
| 73 return builder_to_highest_number[build.builder_name] == build.build_
number |
| 74 |
| 75 return [b for b in builds if is_latest_build(b)] |
| 51 | 76 |
| 52 def changed_files(self, issue_number): | 77 def changed_files(self, issue_number): |
| 53 """Lists the files included in a CL, or None if this can't be determined
. | 78 """Lists the files included in a CL, or None if this can't be determined
. |
| 54 | 79 |
| 55 File paths are sorted and relative to the repository root. | 80 File paths are sorted and relative to the repository root. |
| 56 """ | 81 """ |
| 57 try: | 82 try: |
| 58 url = self._latest_patchset_url(issue_number) | 83 url = self._latest_patchset_url(issue_number) |
| 59 issue_data = self._get_json(url) | 84 issue_data = self._get_json(url) |
| 60 return sorted(issue_data['files']) | 85 return sorted(issue_data['files']) |
| (...skipping 22 matching lines...) Expand all Loading... |
| 83 return json.loads(contents) | 108 return json.loads(contents) |
| 84 except ValueError: | 109 except ValueError: |
| 85 _log.error('Invalid JSON: %s', contents) | 110 _log.error('Invalid JSON: %s', contents) |
| 86 raise | 111 raise |
| 87 | 112 |
| 88 def _issue_url(self, issue_number): | 113 def _issue_url(self, issue_number): |
| 89 return '%s/%s' % (BASE_CODEREVIEW_URL, issue_number) | 114 return '%s/%s' % (BASE_CODEREVIEW_URL, issue_number) |
| 90 | 115 |
| 91 def _patchset_url(self, issue_number, patchset_number): | 116 def _patchset_url(self, issue_number, patchset_number): |
| 92 return '%s/%s' % (self._issue_url(issue_number), patchset_number) | 117 return '%s/%s' % (self._issue_url(issue_number), patchset_number) |
| 93 | |
| 94 def filter_latest_jobs(self, jobs): | |
| 95 """Filters out the list of jobs to include only the latest for each buil
der. | |
| 96 | |
| 97 Args: | |
| 98 jobs: A list of Build objects. | |
| 99 | |
| 100 Returns: | |
| 101 A list of Build objects such that only the latest job for each build
er | |
| 102 is kept. | |
| 103 """ | |
| 104 builder_to_highest_number = {} | |
| 105 for j in jobs: | |
| 106 if j.build_number > builder_to_highest_number.get(j.builder_name, 0)
: | |
| 107 builder_to_highest_number[j.builder_name] = j.build_number | |
| 108 return [j for j in jobs if ( | |
| 109 j.builder_name in builder_to_highest_number and | |
| 110 builder_to_highest_number[j.builder_name] == j.build_number | |
| 111 )] | |
| 112 | |
| 113 def get_latest_try_job_results(self, issue_number): | |
| 114 """Returns a dict mapping builders to try job results.""" | |
| 115 url = self._latest_patchset_url(issue_number) | |
| 116 patchset_data = self._get_json(url) | |
| 117 results = {} | |
| 118 for job in patchset_data['try_job_results']: | |
| 119 results[job['builder']] = job['result'] | |
| 120 return results | |
| OLD | NEW |