1#!/usr/bin/env python 2 3# Copyright 2017 Google Inc. 4# 5# Use of this source code is governed by a BSD-style license that can be 6# found in the LICENSE file. 7 8 9"""Submit one or more try jobs.""" 10 11 12import argparse 13import json 14import os 15import re 16import subprocess 17import sys 18import tempfile 19 20 21BUCKET_SKIA_PRIMARY = 'skia/skia.primary' 22BUCKET_SKIA_INTERNAL = 'skia-internal/skia.internal' 23INFRA_BOTS = os.path.join('infra', 'bots') 24TASKS_JSON = os.path.join(INFRA_BOTS, 'tasks.json') 25REPO_INTERNAL = 'https://skia.googlesource.com/internal_test.git' 26TMP_DIR = os.path.join(tempfile.gettempdir(), 'sktry') 27 28SKIA_ROOT = os.path.realpath(os.path.join( 29 os.path.dirname(os.path.abspath(__file__)), os.pardir)) 30SKIA_INFRA_BOTS = os.path.join(SKIA_ROOT, INFRA_BOTS) 31sys.path.insert(0, SKIA_INFRA_BOTS) 32 33import utils 34 35 36def find_repo_root(): 37 """Find the root directory of the current repository.""" 38 cwd = os.getcwd() 39 while True: 40 if os.path.isdir(os.path.join(cwd, '.git')): 41 return cwd 42 next_cwd = os.path.dirname(cwd) 43 if next_cwd == cwd: 44 raise Exception('Failed to find repo root!') 45 cwd = next_cwd 46 47 48def get_jobs(repo): 49 """Obtain the list of jobs from the given repo.""" 50 # Maintain a copy of the repo in the temp dir. 51 if not os.path.isdir(TMP_DIR): 52 os.mkdir(TMP_DIR) 53 with utils.chdir(TMP_DIR): 54 dirname = repo.split('/')[-1] 55 if not os.path.isdir(dirname): 56 subprocess.check_call([ 57 utils.GIT, 'clone', '--mirror', repo, dirname]) 58 with utils.chdir(dirname): 59 subprocess.check_call([utils.GIT, 'remote', 'update']) 60 jobs = json.loads(subprocess.check_output([ 61 utils.GIT, 'show', 'master:%s' % JOBS_JSON])) 62 return (BUCKET_SKIA_INTERNAL, jobs) 63 64 65def main(): 66 # Parse arguments. 67 d = 'Helper script for triggering try jobs.' 68 parser = argparse.ArgumentParser(description=d) 69 parser.add_argument('--list', action='store_true', default=False, 70 help='Just list the jobs; do not trigger anything.') 71 parser.add_argument('--internal', action='store_true', default=False, 72 help=('If set, include internal jobs. You must have ' 73 'permission to view internal repos.')) 74 parser.add_argument('job', nargs='?', default=None, 75 help='Job name or regular expression to match job names.') 76 args = parser.parse_args() 77 78 # Load and filter the list of jobs. 79 jobs = [] 80 tasks_json = os.path.join(find_repo_root(), TASKS_JSON) 81 with open(tasks_json) as f: 82 tasks_cfg = json.load(f) 83 skia_primary_jobs = [] 84 for k, v in tasks_cfg['jobs'].iteritems(): 85 skia_primary_jobs.append(k) 86 skia_primary_jobs.sort() 87 88 # TODO(borenet): This assumes that the current repo is associated with the 89 # skia.primary bucket. This will work for most repos but it would be better to 90 # look up the correct bucket to use. 91 jobs.append((BUCKET_SKIA_PRIMARY, skia_primary_jobs)) 92 if args.internal: 93 jobs.append(get_jobs(REPO_INTERNAL)) 94 if args.job: 95 filtered_jobs = [] 96 for bucket, job_list in jobs: 97 filtered = [j for j in job_list if re.search(args.job, j)] 98 if len(filtered) > 0: 99 filtered_jobs.append((bucket, filtered)) 100 jobs = filtered_jobs 101 102 # Display the list of jobs. 103 if len(jobs) == 0: 104 print 'Found no jobs matching "%s"' % repr(args.job) 105 sys.exit(1) 106 count = 0 107 for bucket, job_list in jobs: 108 count += len(job_list) 109 print 'Found %d jobs:' % count 110 for bucket, job_list in jobs: 111 print ' %s:' % bucket 112 for j in job_list: 113 print ' %s' % j 114 if args.list: 115 return 116 117 if count > 1: 118 # Prompt before triggering jobs. 119 resp = raw_input('\nDo you want to trigger these jobs? (y/n or i for ' 120 'interactive): ') 121 print '' 122 if resp != 'y' and resp != 'i': 123 sys.exit(1) 124 if resp == 'i': 125 filtered_jobs = [] 126 for bucket, job_list in jobs: 127 new_job_list = [] 128 for j in job_list: 129 incl = raw_input(('Trigger %s? (y/n): ' % j)) 130 if incl == 'y': 131 new_job_list.append(j) 132 if len(new_job_list) > 0: 133 filtered_jobs.append((bucket, new_job_list)) 134 jobs = filtered_jobs 135 136 # Trigger the try jobs. 137 for bucket, job_list in jobs: 138 cmd = ['git', 'cl', 'try', '-B', bucket] 139 for j in job_list: 140 cmd.extend(['-b', j]) 141 try: 142 subprocess.check_call(cmd) 143 except subprocess.CalledProcessError: 144 # Output from the command will fall through, so just exit here rather than 145 # printing a stack trace. 146 sys.exit(1) 147 148 149if __name__ == '__main__': 150 main() 151