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.primary'
22BUCKET_SKIA_INTERNAL = 'skia.internal'
23CHECKOUT_ROOT = os.path.realpath(os.path.join(
24    os.path.dirname(os.path.abspath(__file__)), os.pardir))
25INFRA_BOTS = os.path.join(CHECKOUT_ROOT, 'infra', 'bots')
26JOBS_JSON = os.path.join(INFRA_BOTS, 'jobs.json')
27REPO_INTERNAL = 'https://skia.googlesource.com/internal_test.git'
28TMP_DIR = os.path.join(tempfile.gettempdir(), 'sktry')
29
30sys.path.insert(0, INFRA_BOTS)
31
32import update_meta_config
33import utils
34
35
36def get_jobs(repo):
37  """Obtain the list of jobs from the given repo."""
38  # Maintain a copy of the repo in the temp dir.
39  if not os.path.isdir(TMP_DIR):
40    os.mkdir(TMP_DIR)
41  with utils.chdir(TMP_DIR):
42    dirname = repo.split('/')[-1]
43    if not os.path.isdir(dirname):
44      subprocess.check_call([
45          utils.GIT, 'clone', '--mirror', repo, dirname])
46    with utils.chdir(dirname):
47      subprocess.check_call([utils.GIT, 'remote', 'update'])
48      jobs = json.loads(subprocess.check_output([
49          utils.GIT, 'show', 'master:infra/bots/jobs.json']))
50      return (BUCKET_SKIA_INTERNAL, jobs)
51
52
53def main():
54  # Parse arguments.
55  d = 'Helper script for triggering try jobs defined in %s.' % JOBS_JSON
56  parser = argparse.ArgumentParser(description=d)
57  parser.add_argument('--list', action='store_true', default=False,
58                      help='Just list the jobs; do not trigger anything.')
59  parser.add_argument('--internal', action='store_true', default=False,
60                      help=('If set, include internal jobs. You must have '
61                            'permission to view internal repos.'))
62  parser.add_argument('job', nargs='?', default=None,
63                      help='Job name or regular expression to match job names.')
64  args = parser.parse_args()
65
66  # Load and filter the list of jobs.
67  jobs = []
68  with open(JOBS_JSON) as f:
69    jobs.append((BUCKET_SKIA_PRIMARY, json.load(f)))
70  if args.internal:
71    jobs.append(get_jobs(REPO_INTERNAL))
72  jobs.extend(update_meta_config.CQ_INCLUDE_CHROMIUM_TRYBOTS)
73  if args.job:
74    filtered_jobs = []
75    for bucket, job_list in jobs:
76      filtered = [j for j in job_list if re.search(args.job, j)]
77      if len(filtered) > 0:
78        filtered_jobs.append((bucket, filtered))
79    jobs = filtered_jobs
80
81  # Display the list of jobs.
82  if len(jobs) == 0:
83    print 'Found no jobs matching "%s"' % repr(args.job)
84    sys.exit(1)
85  count = 0
86  for bucket, job_list in jobs:
87    count += len(job_list)
88  print 'Found %d jobs:' % count
89  for bucket, job_list in jobs:
90    print '  %s:' % bucket
91    for j in job_list:
92      print '    %s' % j
93  if args.list:
94    return
95
96  if count > 1:
97    # Prompt before triggering jobs.
98    resp = raw_input('\nDo you want to trigger these jobs? (y/n or i for '
99                     'interactive): ')
100    print ''
101    if resp != 'y' and resp != 'i':
102      sys.exit(1)
103    if resp == 'i':
104      filtered_jobs = []
105      for bucket, job_list in jobs:
106        new_job_list = []
107        for j in job_list:
108          incl = raw_input(('Trigger %s? (y/n): ' % j))
109          if incl == 'y':
110            new_job_list.append(j)
111        if len(new_job_list) > 0:
112          filtered_jobs.append((bucket, new_job_list))
113      jobs = filtered_jobs
114
115  # Trigger the try jobs.
116  for bucket, job_list in jobs:
117    cmd = ['git', 'cl', 'try', '-B', bucket]
118    for j in job_list:
119      cmd.extend(['-b', j])
120    try:
121      subprocess.check_call(cmd)
122    except subprocess.CalledProcessError:
123      # Output from the command will fall through, so just exit here rather than
124      # printing a stack trace.
125      sys.exit(1)
126
127
128if __name__ == '__main__':
129  main()
130