OLD | NEW |
---|---|
(Empty) | |
1 #!/usr/bin/env python | |
2 # Copyright (c) 2013 The Chromium Authors. All rights reserved. | |
3 # Use of this source code is governed by a BSD-style license that can be | |
4 # found in the LICENSE file. | |
5 | |
6 """Client-side script to send local git changes to a tryserver. | |
7 | |
8 It pushes the local feature branch to a private ref on googlesource | |
9 and posts a description of the job to an appengine instance, where it will get | |
10 picked up by the buildbot tryserver itself. | |
11 """ | |
12 | |
13 from __future__ import print_function | |
14 | |
15 import json | |
16 import os | |
17 import subprocess | |
18 import sys | |
19 import urllib | |
20 | |
21 | |
22 def DieWithError(msg): | |
23 """Prints the message to stderr and exits.""" | |
24 print(msg, file=sys.stderr) | |
25 sys.exit(1) | |
26 | |
27 | |
28 def RunGit(*args, **kwargs): | |
29 """Runs the given git command with arguments, or dies. | |
30 | |
31 Passes the given kwargs (e.g. cwd or env) through to subprocess.""" | |
32 cmd = ('git',) + args | |
33 try: | |
34 return subprocess.check_output(cmd, **kwargs).strip() | |
35 except subprocess.CalledProcessError as e: | |
36 DieWithError('Command "%s" failed.\n%s' % (' '.join(cmd), e)) | |
37 | |
38 | |
39 def EnsureInGitRepo(): | |
40 """Quick sanity check to make sure we're in a git repo.""" | |
41 if not RunGit('rev-parse', '--is-inside-work-tree') == 'true': | |
42 DieWithError('You don\'t appear to be inside a git repository.') | |
43 | |
44 | |
45 def GetCodeReviewSettings(): | |
46 """Reads codereview.settings and returns a dict of settings.""" | |
47 top_dir = RunGit('rev-parse', '--show-toplevel') | |
48 this_dir = os.getcwd() | |
49 assert this_dir.startswith(top_dir), (top_dir, this_dir) | |
50 | |
51 settings_file = os.path.join(this_dir, 'codereview.settings') | |
52 while not os.path.isfile(settings_file): | |
53 this_dir = os.path.split(this_dir)[0] | |
54 if not this_dir.startswith(top_dir): | |
55 DieWithError('Unable to find codereview.settings in this repo.') | |
56 settings_file = os.path.join(this_dir, 'codereview.settings') | |
57 | |
58 settings = {} | |
59 with open(settings_file, 'r') as f: | |
60 for line in f.readlines(): | |
61 if line.startswith('#'): | |
62 continue | |
63 k, v = line.split(':', 1) | |
64 settings[k.strip()] = v.strip() | |
65 return settings | |
66 | |
67 | |
68 def PushBranch(): | |
69 """Pushes the current local branch to a ref in the try repo. | |
70 | |
71 The try repo is either the remote called 'try', or 'origin' otherwise. | |
72 The ref is '/refs/try/<username>/<local branch>-<short hash>. | |
73 | |
74 Returns the ref to which the local branch was pushed.""" | |
75 username = RunGit('config', '--get', 'user.email').split('@', 1)[0] | |
76 branch = RunGit('symbolic-ref', '--short', '-q', 'HEAD') | |
77 commit = RunGit('rev-parse', branch)[:8] | |
78 remotes = RunGit('remote').splitlines() | |
79 if not all((username, branch, commit, remotes)): | |
80 DieWithError('Unable to get necessary git configuration.') | |
81 | |
82 remote = 'try' if 'try' in remotes else 'origin' | |
83 ref = 'refs/try/%s/%s-%s' % (username, branch, commit) | |
84 | |
85 RunGit('push', remote, '%s:%s' % (branch, ref)) | |
86 return ref | |
87 | |
88 | |
89 def MakeJob(project, jobname, ref): | |
90 """Creates a job description blob.""" | |
91 email = RunGit('config', '--get', 'user.email') | |
92 repository = RunGit('config', '--get', 'remote.origin.url') | |
93 job = { | |
94 # Fields for buildbot sourcestamp. | |
95 'project': project, | |
96 'repository': repository, | |
97 'branch': ref, | |
98 'revision': 'HEAD', | |
99 # Fields for buildbot builder factory. | |
100 'buildername': jobname, | |
101 'recipe': project, | |
102 # Other useful fields. | |
103 'blamelist': [email], | |
104 } | |
105 return json.dumps(job) | |
106 | |
107 | |
108 def PostJob(server, project, job): | |
109 """POSTs the job description blob to the tryserver instance.""" | |
110 url = '%s/%s/push' % (server, project) | |
111 data = urllib.urlencode({'job': job}) | |
112 try: | |
113 conn = urllib.urlopen(url, data) | |
114 except IOError as e: | |
115 DieWithError(e) | |
116 response = conn.getcode() | |
117 if response != 200: | |
118 DieWithError('Failed to POST. Got: %d' % response) | |
119 | |
120 | |
121 def Main(argv): | |
122 """Main entry point.""" | |
123 EnsureInGitRepo() | |
124 | |
125 settings = GetCodeReviewSettings() | |
126 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.
| |
127 project = settings.get('TRYSERVER_PROJECT') | |
128 jobnames = settings.get('TRYSERVER_JOB_NAME') | |
129 if not all((server, project, jobnames)): | |
130 DieWithError('Missing configuration in codereview.settings.') | |
131 | |
132 ref = PushBranch() | |
133 for jobname in jobnames.split(','): | |
134 job = MakeJob(project, jobname, ref) | |
135 PostJob(server, project, job) | |
136 | |
137 | |
138 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.
| |
139 sys.exit(Main(sys.argv)) | |
OLD | NEW |