Index: trychange_git.py |
diff --git a/trychange_git.py b/trychange_git.py |
new file mode 100755 |
index 0000000000000000000000000000000000000000..6a324dc36ddc73ff0fbd32459bfa3b5fdef54e15 |
--- /dev/null |
+++ b/trychange_git.py |
@@ -0,0 +1,139 @@ |
+#!/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.""" |
+ 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') |
cmp
2013/08/20 05:52:29
It would be for the best to enforce that TRYSERVER
agable
2013/08/20 07:48:08
Done.
|
+ 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__": |
cmp
2013/08/20 05:52:29
single quotes here instead of double quotes
agable
2013/08/20 07:48:08
Done.
|
+ sys.exit(Main(sys.argv)) |