Create directory for appengine-based tryserver. Also add the upload utility, trychange_git.py. This change has already been reviewed and approved at https://codereview.chromium.org/23205010/ BUG=276183 TBR=cmp@chromium.org Review URL: https://codereview.chromium.org/23242010 git-svn-id: svn://svn.chromium.org/chrome/trunk/tools/chromium-jobqueue@218721 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/trychange_git.py b/trychange_git.py new file mode 100755 index 0000000..28f708b --- /dev/null +++ b/trychange_git.py
@@ -0,0 +1,141 @@ +#!/usr/bin/env python +# Copyright (c) 2013 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""Client-side script to send local git changes to a tryserver. + +It pushes the local feature branch to a private ref on googlesource +and posts a description of the job to an appengine instance, where it will get +picked up by the buildbot tryserver itself. +""" + +from __future__ import print_function + +import json +import os +import subprocess +import sys +import urllib + + +def DieWithError(msg): + """Prints the message to stderr and exits.""" + print(msg, file=sys.stderr) + sys.exit(1) + + +def RunGit(*args, **kwargs): + """Runs the given git command with arguments, or dies. + + Passes the given kwargs (e.g. cwd or env) through to subprocess.""" + cmd = ('git',) + args + try: + return subprocess.check_output(cmd, **kwargs).strip() + except subprocess.CalledProcessError as e: + DieWithError('Command "%s" failed.\n%s' % (' '.join(cmd), e)) + + +def EnsureInGitRepo(): + """Quick sanity check to make sure we're in a git repo.""" + if not RunGit('rev-parse', '--is-inside-work-tree') == 'true': + DieWithError('You don\'t appear to be inside a git repository.') + + +def GetCodeReviewSettings(): + """Reads codereview.settings and returns a dict of settings.""" + top_dir = RunGit('rev-parse', '--show-toplevel') + this_dir = os.getcwd() + assert this_dir.startswith(top_dir), (top_dir, this_dir) + + settings_file = os.path.join(this_dir, 'codereview.settings') + while not os.path.isfile(settings_file): + this_dir = os.path.split(this_dir)[0] + if not this_dir.startswith(top_dir): + DieWithError('Unable to find codereview.settings in this repo.') + settings_file = os.path.join(this_dir, 'codereview.settings') + + settings = {} + with open(settings_file, 'r') as f: + for line in f.readlines(): + if line.startswith('#'): + continue + k, v = line.split(':', 1) + settings[k.strip()] = v.strip() + return settings + + +def PushBranch(): + """Pushes the current local branch to a ref in the try repo. + + The try repo is either the remote called 'try', or 'origin' otherwise. + The ref is '/refs/try/<username>/<local branch>-<short hash>. + + Returns the ref to which the local branch was pushed.""" + username = RunGit('config', '--get', 'user.email').split('@', 1)[0] + branch = RunGit('symbolic-ref', '--short', '-q', 'HEAD') + commit = RunGit('rev-parse', branch)[:8] + remotes = RunGit('remote').splitlines() + if not all((username, branch, commit, remotes)): + DieWithError('Unable to get necessary git configuration.') + + remote = 'try' if 'try' in remotes else 'origin' + ref = 'refs/try/%s/%s-%s' % (username, branch, commit) + + RunGit('push', remote, '%s:%s' % (branch, ref)) + return ref + + +def MakeJob(project, jobname, ref): + """Creates a job description blob.""" + email = RunGit('config', '--get', 'user.email') + repository = RunGit('config', '--get', 'remote.origin.url') + job = { + # Fields for buildbot sourcestamp. + 'project': project, + 'repository': repository, + 'branch': ref, + 'revision': 'HEAD', + # Fields for buildbot builder factory. + 'buildername': jobname, + 'recipe': project, + # Other useful fields. + 'blamelist': [email], + } + return json.dumps(job) + + +def PostJob(server, project, job): + """POSTs the job description blob to the tryserver instance.""" + if not server.startswith('https://'): + DieWithError('Server URL must be https.') + url = '%s/%s/push' % (server, project) + data = urllib.urlencode({'job': job}) + try: + conn = urllib.urlopen(url, data) + except IOError as e: + DieWithError(e) + response = conn.getcode() + if response != 200: + DieWithError('Failed to POST. Got: %d' % response) + + +def Main(_argv): + """Main entry point.""" + EnsureInGitRepo() + + settings = GetCodeReviewSettings() + server = settings.get('TRYSERVER_HTTP_HOST') + project = settings.get('TRYSERVER_PROJECT') + jobnames = settings.get('TRYSERVER_JOB_NAME') + if not all((server, project, jobnames)): + DieWithError('Missing configuration in codereview.settings.') + + ref = PushBranch() + for jobname in jobnames.split(','): + job = MakeJob(project, jobname, ref) + PostJob(server, project, job) + + +if __name__ == '__main__': + sys.exit(Main(sys.argv))