skia2/bin/try.py
Eric Boren d7e55629a0 bin/try: automatically set 'git cl issue' when not found
This eases the workflow for developers who don't use git-cl to upload
changes, but it doesn't remove the dependency on git-cl for triggering
try jobs. Doing so would require using the Buildbucket API directly,
which we're not well set up to do in Python. Eventually we'll be forced
to either migrate this tool to Python 3 or rewrite in Go, and at that
point we can use the API clients we already use from Go code to remove
the dependency on Depot Tools.

Change-Id: I749007a2150cfeb2442643db6f2a01a7a56c10a4
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/298748
Commit-Queue: Eric Boren <borenet@google.com>
Reviewed-by: Mike Klein <mtklein@google.com>
2020-06-25 17:44:01 +00:00

177 lines
5.8 KiB
Python
Executable File

#!/usr/bin/env python
# Copyright 2017 Google Inc.
#
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Submit one or more try jobs."""
import argparse
import json
import os
import re
import subprocess
import sys
import tempfile
import urllib2
BUCKET_SKIA_PRIMARY = 'skia/skia.primary'
BUCKET_SKIA_INTERNAL = 'skia-internal/skia.internal'
INFRA_BOTS = os.path.join('infra', 'bots')
TASKS_JSON = os.path.join(INFRA_BOTS, 'tasks.json')
REPO_INTERNAL = 'https://skia.googlesource.com/internal_test.git'
TMP_DIR = os.path.join(tempfile.gettempdir(), 'sktry')
SKIA_ROOT = os.path.realpath(os.path.join(
os.path.dirname(os.path.abspath(__file__)), os.pardir))
SKIA_INFRA_BOTS = os.path.join(SKIA_ROOT, INFRA_BOTS)
sys.path.insert(0, SKIA_INFRA_BOTS)
import utils
def find_repo_root():
"""Find the root directory of the current repository."""
cwd = os.getcwd()
while True:
if os.path.isdir(os.path.join(cwd, '.git')):
return cwd
next_cwd = os.path.dirname(cwd)
if next_cwd == cwd:
raise Exception('Failed to find repo root!')
cwd = next_cwd
def get_jobs(repo):
"""Obtain the list of jobs from the given repo."""
# Maintain a copy of the repo in the temp dir.
if not os.path.isdir(TMP_DIR):
os.mkdir(TMP_DIR)
with utils.chdir(TMP_DIR):
dirname = repo.split('/')[-1]
if not os.path.isdir(dirname):
subprocess.check_call([
utils.GIT, 'clone', '--mirror', repo, dirname])
with utils.chdir(dirname):
subprocess.check_call([utils.GIT, 'remote', 'update'])
jobs = json.loads(subprocess.check_output([
utils.GIT, 'show', 'master:%s' % JOBS_JSON]))
return (BUCKET_SKIA_INTERNAL, jobs)
def main():
# Parse arguments.
d = 'Helper script for triggering try jobs.'
parser = argparse.ArgumentParser(description=d)
parser.add_argument('--list', action='store_true', default=False,
help='Just list the jobs; do not trigger anything.')
parser.add_argument('--internal', action='store_true', default=False,
help=('If set, include internal jobs. You must have '
'permission to view internal repos.'))
parser.add_argument('job', nargs='?', default=None,
help='Job name or regular expression to match job names.')
args = parser.parse_args()
# First, find the Gerrit issue number. If the change was uploaded using Depot
# Tools, this configuration will be present in the git config.
branch = subprocess.check_output(['git', 'branch', '--show-current']).rstrip()
if not branch:
print 'Not on any branch; cannot trigger try jobs.'
sys.exit(1)
branch_issue_config = 'branch.%s.gerritissue' % branch
try:
issue = subprocess.check_output([
'git', 'config', '--local', branch_issue_config])
except subprocess.CalledProcessError:
# Not using Depot Tools. Find the Change-Id line in the most recent commit
# and obtain the issue number using that.
print '"git cl issue" not set; searching for Change-Id footer.'
msg = subprocess.check_output(['git', 'log', '-n1', branch])
m = re.search('Change-Id: (I[a-f0-9]+)', msg)
if not m:
print ('No gerrit issue found in `git config --local %s` and no Change-Id'
' found in most recent commit message.')
sys.exit(1)
url = 'https://skia-review.googlesource.com/changes/%s' % m.groups()[0]
resp = urllib2.urlopen(url).read()
issue = str(json.loads('\n'.join(resp.splitlines()[1:]))['_number'])
print 'Setting "git cl issue %s"' % issue
subprocess.check_call(['git', 'cl', 'issue', issue])
# Load and filter the list of jobs.
jobs = []
tasks_json = os.path.join(find_repo_root(), TASKS_JSON)
with open(tasks_json) as f:
tasks_cfg = json.load(f)
skia_primary_jobs = []
for k, v in tasks_cfg['jobs'].iteritems():
skia_primary_jobs.append(k)
skia_primary_jobs.sort()
# TODO(borenet): This assumes that the current repo is associated with the
# skia.primary bucket. This will work for most repos but it would be better to
# look up the correct bucket to use.
jobs.append((BUCKET_SKIA_PRIMARY, skia_primary_jobs))
if args.internal:
jobs.append(get_jobs(REPO_INTERNAL))
if args.job:
filtered_jobs = []
for bucket, job_list in jobs:
filtered = [j for j in job_list if re.search(args.job, j)]
if len(filtered) > 0:
filtered_jobs.append((bucket, filtered))
jobs = filtered_jobs
# Display the list of jobs.
if len(jobs) == 0:
print 'Found no jobs matching "%s"' % repr(args.job)
sys.exit(1)
count = 0
for bucket, job_list in jobs:
count += len(job_list)
print 'Found %d jobs:' % count
for bucket, job_list in jobs:
print ' %s:' % bucket
for j in job_list:
print ' %s' % j
if args.list:
return
if count > 1:
# Prompt before triggering jobs.
resp = raw_input('\nDo you want to trigger these jobs? (y/n or i for '
'interactive): ')
print ''
if resp != 'y' and resp != 'i':
sys.exit(1)
if resp == 'i':
filtered_jobs = []
for bucket, job_list in jobs:
new_job_list = []
for j in job_list:
incl = raw_input(('Trigger %s? (y/n): ' % j))
if incl == 'y':
new_job_list.append(j)
if len(new_job_list) > 0:
filtered_jobs.append((bucket, new_job_list))
jobs = filtered_jobs
# Trigger the try jobs.
for bucket, job_list in jobs:
cmd = ['git', 'cl', 'try', '-B', bucket]
for j in job_list:
cmd.extend(['-b', j])
try:
subprocess.check_call(cmd)
except subprocess.CalledProcessError:
# Output from the command will fall through, so just exit here rather than
# printing a stack trace.
sys.exit(1)
if __name__ == '__main__':
main()