| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203 | 
							- #!/usr/bin/env python
 
- # Copyright 2017 gRPC authors.
 
- #
 
- # Licensed under the Apache License, Version 2.0 (the "License");
 
- # you may not use this file except in compliance with the License.
 
- # You may obtain a copy of the License at
 
- #
 
- #     http://www.apache.org/licenses/LICENSE-2.0
 
- #
 
- # Unless required by applicable law or agreed to in writing, software
 
- # distributed under the License is distributed on an "AS IS" BASIS,
 
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 
- # See the License for the specific language governing permissions and
 
- # limitations under the License.
 
- """Measure the time between PR creation and completion of all tests.
 
- You'll need a github API token to avoid being rate-limited. See
 
- https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line/
 
- This script goes over the most recent 100 pull requests. For PRs with a single
 
- commit, it uses the PR's creation as the initial time; othewise, it uses the
 
- date of the last commit. This is somewhat fragile, and imposed by the fact that
 
- GitHub reports a PR's updated timestamp for any event that modifies the PR (e.g.
 
- comments), not just the addition of new commits.
 
- In addition, it ignores latencies greater than five hours, as that's likely due
 
- to a manual re-run of tests.
 
- """
 
- from __future__ import absolute_import
 
- from __future__ import division
 
- from __future__ import print_function
 
- import json
 
- import logging
 
- import pprint
 
- import urllib2
 
- from datetime import datetime, timedelta
 
- logging.basicConfig(format='%(asctime)s %(message)s')
 
- PRS = 'https://api.github.com/repos/grpc/grpc/pulls?state=open&per_page=100'
 
- COMMITS = 'https://api.github.com/repos/grpc/grpc/pulls/{pr_number}/commits'
 
- def gh(url):
 
-     request = urllib2.Request(url)
 
-     if TOKEN:
 
-         request.add_header('Authorization', 'token {}'.format(TOKEN))
 
-     response = urllib2.urlopen(request)
 
-     return response.read()
 
- def print_csv_header():
 
-     print('pr,base_time,test_time,latency_seconds,successes,failures,errors')
 
- def output(pr,
 
-            base_time,
 
-            test_time,
 
-            diff_time,
 
-            successes,
 
-            failures,
 
-            errors,
 
-            mode='human'):
 
-     if mode == 'human':
 
-         print(
 
-             "PR #{} base time: {} UTC, Tests completed at: {} UTC. Latency: {}."
 
-             "\n\tSuccesses: {}, Failures: {}, Errors: {}".format(
 
-                 pr, base_time, test_time, diff_time, successes, failures,
 
-                 errors))
 
-     elif mode == 'csv':
 
-         print(','.join([
 
-             str(pr),
 
-             str(base_time),
 
-             str(test_time),
 
-             str(int((test_time - base_time).total_seconds())),
 
-             str(successes),
 
-             str(failures),
 
-             str(errors)
 
-         ]))
 
- def parse_timestamp(datetime_str):
 
-     return datetime.strptime(datetime_str, '%Y-%m-%dT%H:%M:%SZ')
 
- def to_posix_timestamp(dt):
 
-     return str((dt - datetime(1970, 1, 1)).total_seconds())
 
- def get_pr_data():
 
-     latest_prs = json.loads(gh(PRS))
 
-     res = [{
 
-         'number': pr['number'],
 
-         'created_at': parse_timestamp(pr['created_at']),
 
-         'updated_at': parse_timestamp(pr['updated_at']),
 
-         'statuses_url': pr['statuses_url']
 
-     } for pr in latest_prs]
 
-     return res
 
- def get_commits_data(pr_number):
 
-     commits = json.loads(gh(COMMITS.format(pr_number=pr_number)))
 
-     return {
 
-         'num_commits': len(commits),
 
-         'most_recent_date':
 
-         parse_timestamp(commits[-1]['commit']['author']['date'])
 
-     }
 
- def get_status_data(statuses_url, system):
 
-     status_url = statuses_url.replace('statuses', 'status')
 
-     statuses = json.loads(gh(status_url + '?per_page=100'))
 
-     successes = 0
 
-     failures = 0
 
-     errors = 0
 
-     latest_datetime = None
 
-     if not statuses: return None
 
-     if system == 'kokoro': string_in_target_url = 'kokoro'
 
-     elif system == 'jenkins': string_in_target_url = 'grpc-testing'
 
-     for status in statuses['statuses']:
 
-         if not status['target_url'] or string_in_target_url not in status['target_url']:
 
-             continue  # Ignore jenkins
 
-         if status['state'] == 'pending': return None
 
-         elif status['state'] == 'success': successes += 1
 
-         elif status['state'] == 'failure': failures += 1
 
-         elif status['state'] == 'error': errors += 1
 
-         if not latest_datetime:
 
-             latest_datetime = parse_timestamp(status['updated_at'])
 
-         else:
 
-             latest_datetime = max(latest_datetime,
 
-                                   parse_timestamp(status['updated_at']))
 
-     # First status is the most recent one.
 
-     if any([successes, failures, errors
 
-            ]) and sum([successes, failures, errors]) > 15:
 
-         return {
 
-             'latest_datetime': latest_datetime,
 
-             'successes': successes,
 
-             'failures': failures,
 
-             'errors': errors
 
-         }
 
-     else:
 
-         return None
 
- def build_args_parser():
 
-     import argparse
 
-     parser = argparse.ArgumentParser()
 
-     parser.add_argument(
 
-         '--format',
 
-         type=str,
 
-         choices=['human', 'csv'],
 
-         default='human',
 
-         help='Output format: are you a human or a machine?')
 
-     parser.add_argument(
 
-         '--system',
 
-         type=str,
 
-         choices=['jenkins', 'kokoro'],
 
-         required=True,
 
-         help='Consider only the given CI system')
 
-     parser.add_argument(
 
-         '--token',
 
-         type=str,
 
-         default='',
 
-         help='GitHub token to use its API with a higher rate limit')
 
-     return parser
 
- def main():
 
-     import sys
 
-     global TOKEN
 
-     args_parser = build_args_parser()
 
-     args = args_parser.parse_args()
 
-     TOKEN = args.token
 
-     if args.format == 'csv': print_csv_header()
 
-     for pr_data in get_pr_data():
 
-         commit_data = get_commits_data(pr_data['number'])
 
-         # PR with a single commit -> use the PRs creation time.
 
-         # else -> use the latest commit's date.
 
-         base_timestamp = pr_data['updated_at']
 
-         if commit_data['num_commits'] > 1:
 
-             base_timestamp = commit_data['most_recent_date']
 
-         else:
 
-             base_timestamp = pr_data['created_at']
 
-         last_status = get_status_data(pr_data['statuses_url'], args.system)
 
-         if last_status:
 
-             diff = last_status['latest_datetime'] - base_timestamp
 
-             if diff < timedelta(hours=5):
 
-                 output(
 
-                     pr_data['number'],
 
-                     base_timestamp,
 
-                     last_status['latest_datetime'],
 
-                     diff,
 
-                     last_status['successes'],
 
-                     last_status['failures'],
 
-                     last_status['errors'],
 
-                     mode=args.format)
 
- if __name__ == '__main__':
 
-     main()
 
 
  |