Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(18)

Side by Side Diff: trychange.py

Issue 501143: Remove gclient-specific hacks from trychange into gcl. (Closed)
Patch Set: Small unit test fix Created 11 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« gcl.py ('K') | « tests/trychange_unittest.py ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/python 1 #!/usr/bin/python
2 # Copyright (c) 2009 The Chromium Authors. All rights reserved. 2 # Copyright (c) 2009 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 """Client-side script to send a try job to the try server. It communicates to 5 """Client-side script to send a try job to the try server. It communicates to
6 the try server by either writting to a svn repository or by directly connecting 6 the try server by either writting to a svn repository or by directly connecting
7 to the server by HTTP. 7 to the server by HTTP.
8 """ 8 """
9 9
10 import datetime 10 import datetime
11 import getpass 11 import getpass
12 import logging 12 import logging
13 import optparse 13 import optparse
14 import os 14 import os
15 import shutil 15 import shutil
16 import socket 16 import socket
17 import subprocess 17 import subprocess
18 import sys 18 import sys
19 import tempfile 19 import tempfile
20 import urllib 20 import urllib
21 21
22 import breakpad 22 import breakpad
23 23
24 import gcl 24 import gcl
25 import gclient_utils 25 import gclient_utils
26 import scm 26 import scm
27 import presubmit_support 27 import presubmit_support
28 import upload
29 28
30 __version__ = '1.1.2' 29 __version__ = '1.2'
31 30
32 31
33 # Constants 32 # Constants
34 HELP_STRING = "Sorry, Tryserver is not available." 33 HELP_STRING = "Sorry, Tryserver is not available."
35 USAGE = r"""%prog [change_name] [options] 34 USAGE = r"""%prog [change_name] [options]
36 35
37 Client-side script to send a try job to the try server. It communicates to 36 Client-side script to send a try job to the try server. It communicates to
38 the try server by either writting to a svn repository or by directly connecting 37 the try server by either writting to a svn repository or by directly connecting
39 to the server by HTTP. 38 to the server by HTTP.
40 39
(...skipping 18 matching lines...) Expand all
59 class InvalidScript(Exception): 58 class InvalidScript(Exception):
60 def __str__(self): 59 def __str__(self):
61 return self.args[0] + '\n' + HELP_STRING 60 return self.args[0] + '\n' + HELP_STRING
62 61
63 62
64 class NoTryServerAccess(Exception): 63 class NoTryServerAccess(Exception):
65 def __str__(self): 64 def __str__(self):
66 return self.args[0] + '\n' + HELP_STRING 65 return self.args[0] + '\n' + HELP_STRING
67 66
68 67
69 def PathDifference(root, subpath):
70 """Returns the difference subpath minus root."""
71 if subpath.find(root) != 0:
72 return None
73 # If the root does not have a trailing \ or /, we add it so the returned path
74 # starts immediately after the seperator regardless of whether it is provided.
75 if not root.endswith(os.sep):
76 root += os.sep
77 return subpath[len(root):]
78
79
80 def GetSourceRoot():
81 """Returns the absolute directory one level up from the repository root."""
82 # TODO(maruel): This is odd to assume that '..' is the source root.
83 return os.path.abspath(os.path.join(gcl.GetRepositoryRoot(), '..'))
84
85
86 def GetTryServerSettings(): 68 def GetTryServerSettings():
87 """Grab try server settings local to the repository.""" 69 """Grab try server settings local to the repository."""
88 def _SafeResolve(host): 70 def _SafeResolve(host):
89 try: 71 try:
90 return socket.getaddrinfo(host, None) 72 return socket.getaddrinfo(host, None)
91 except socket.gaierror: 73 except socket.gaierror:
92 return None 74 return None
93 75
94 settings = {} 76 settings = {}
95 settings['http_port'] = gcl.GetCodeReviewSetting('TRYSERVER_HTTP_PORT') 77 settings['http_port'] = gcl.GetCodeReviewSetting('TRYSERVER_HTTP_PORT')
(...skipping 21 matching lines...) Expand all
117 99
118 def EscapeDot(name): 100 def EscapeDot(name):
119 return name.replace('.', '-') 101 return name.replace('.', '-')
120 102
121 103
122 class SCM(object): 104 class SCM(object):
123 """Simplistic base class to implement one function: ProcessOptions.""" 105 """Simplistic base class to implement one function: ProcessOptions."""
124 def __init__(self, options): 106 def __init__(self, options):
125 self.options = options 107 self.options = options
126 108
127 def ProcessOptions(self): 109 def GetFileNames(self):
128 raise NotImplementedError 110 """Return the list of files in the diff."""
111 return self.options.files
129 112
130 113
131 class SVN(SCM): 114 class SVN(SCM):
132 """Gathers the options and diff for a subversion checkout.""" 115 """Gathers the options and diff for a subversion checkout."""
133 def GenerateDiff(self, files, root): 116 def __init__(self, *args, **kwargs):
117 SCM.__init__(self, *args, **kwargs)
118 self.checkout_root = scm.SVN.GetCheckoutRoot(os.getcwd())
119 self.options.files
120 if not self.options.diff:
121 # Generate the diff from the scm.
122 self.options.diff = self._GenerateDiff()
123 if not self.options.email:
124 # Assumes the svn credential is an email address.
125 self.options.email = scm.SVN.GetEmail(self.checkout_root)
126
127 def _GenerateDiff(self):
134 """Returns a string containing the diff for the given file list. 128 """Returns a string containing the diff for the given file list.
135 129
136 The files in the list should either be absolute paths or relative to the 130 The files in the list should either be absolute paths or relative to the
137 given root. If no root directory is provided, the repository root will be 131 given root.
138 used.
139 """ 132 """
140 previous_cwd = os.getcwd() 133 previous_cwd = os.getcwd()
141 os.chdir(root or scm.SVN.GetCheckoutRoot(previous_cwd)) 134 os.chdir(self.checkout_root)
142 135 if not self.options.files:
136 self.options.files = [f[1] for f in scm.SVN.CaptureStatus(None)]
143 # Directories will return None so filter them out. 137 # Directories will return None so filter them out.
144 diff = filter(None, [scm.SVN.DiffItem(f) for f in files]) 138 diff = filter(None, [scm.SVN.DiffItem(f) for f in self.options.files])
145 os.chdir(previous_cwd) 139 os.chdir(previous_cwd)
146 return "".join(diff) 140 return "".join(diff)
147 141
148 def GetFileNames(self):
149 """Return the list of files in the diff."""
150 return self.change_info.GetFileNames()
151
152 def GetLocalRoot(self): 142 def GetLocalRoot(self):
153 """Return the path of the repository root.""" 143 """Return the path of the repository root."""
154 return self.change_info.GetLocalRoot() 144 return self.checkout_root
155
156 def ProcessOptions(self):
157 checkout_root = None
158 if not self.options.diff:
159 # Generate the diff with svn and write it to the submit queue path. The
160 # files are relative to the repository root, but we need patches relative
161 # to one level up from there (i.e., 'src'), so adjust both the file
162 # paths and the root of the diff.
163 # TODO(maruel): Remove this hack.
164 source_root = GetSourceRoot()
165 checkout_root = scm.SVN.GetCheckoutRoot(os.getcwd())
166 prefix = PathDifference(source_root, checkout_root)
167 adjusted_paths = [os.path.join(prefix, x) for x in self.options.files]
168 self.options.diff = self.GenerateDiff(adjusted_paths, root=source_root)
169 self.change_info = gcl.LoadChangelistInfoForMultiple(self.options.name,
170 gcl.GetRepositoryRoot(), True, True)
171 if not self.options.email:
172 checkout_root = checkout_root or scm.SVN.GetCheckoutRoot(os.getcwd())
173 self.options.email = scm.SVN.GetEmail(checkout_root)
174 145
175 146
176 class GIT(SCM): 147 class GIT(SCM):
177 """Gathers the options and diff for a git checkout.""" 148 """Gathers the options and diff for a git checkout."""
178 def GenerateDiff(self): 149 def __init__(self, *args, **kwargs):
150 SCM.__init__(self, *args, **kwargs)
151 self.checkout_root = os.path.abspath(
152 gclient_utils.CheckCall(['git', 'rev-parse', '--show-cdup']).strip())
153 if not self.options.diff:
154 self.options.diff = self._GenerateDiff()
155 if not self.options.name:
156 self.options.name = self._GetPatchName()
157 if not self.options.email:
158 self.options.email = scm.GIT.GetEmail('.')
159
160 def _GenerateDiff(self):
179 """Get the diff we'll send to the try server. We ignore the files list.""" 161 """Get the diff we'll send to the try server. We ignore the files list."""
180 branch = upload.RunShell(['git', 'cl', 'upstream']).strip() 162 branch = gclient_utils.CheckCall(['git', 'cl', 'upstream']).strip()
181 diff = upload.RunShell(['git', 'diff-tree', '-p', '--no-prefix', 163 diff = gclient_utils.CheckCall(['git', 'diff-tree', '-p', '--no-prefix',
182 branch, 'HEAD']).splitlines(True) 164 branch, 'HEAD']).splitlines(True)
183 for i in range(len(diff)): 165 for i in range(len(diff)):
184 # In the case of added files, replace /dev/null with the path to the 166 # In the case of added files, replace /dev/null with the path to the
185 # file being added. 167 # file being added.
186 if diff[i].startswith('--- /dev/null'): 168 if diff[i].startswith('--- /dev/null'):
187 diff[i] = '--- %s' % diff[i+1][4:] 169 diff[i] = '--- %s' % diff[i+1][4:]
188 return ''.join(diff) 170 return ''.join(diff)
189 171
190 def GetFileNames(self): 172 def _GetPatchName(self):
191 """Return the list of files in the diff."""
192 return self.options.files
193
194 def GetLocalRoot(self):
195 """Return the path of the repository root."""
196 # TODO: check for errors here?
197 root = upload.RunShell(['git', 'rev-parse', '--show-cdup']).strip()
198 return os.path.abspath(root)
199
200 def GetPatchName(self):
201 """Construct a name for this patch.""" 173 """Construct a name for this patch."""
202 # TODO: perhaps include the hash of the current commit, to distinguish 174 # TODO: perhaps include the hash of the current commit, to distinguish
203 # patches? 175 # patches?
204 branch = upload.RunShell(['git', 'symbolic-ref', 'HEAD']).strip() 176 branch = gclient_utils.CheckCall(['git', 'symbolic-ref', 'HEAD']).strip()
205 if not branch.startswith('refs/heads/'): 177 if not branch.startswith('refs/heads/'):
206 # TODO(maruel): Find a better type. 178 # TODO(maruel): Find a better type.
207 raise NoTryServerAccess("Couldn't figure out branch name") 179 raise NoTryServerAccess("Couldn't figure out branch name")
208 branch = branch[len('refs/heads/'):] 180 branch = branch[len('refs/heads/'):]
209 return branch 181 return branch
210 182
211 def ProcessOptions(self): 183 def GetLocalRoot(self):
212 if not self.options.diff: 184 """Return the path of the repository root."""
213 self.options.diff = self.GenerateDiff() 185 return self.checkout_root
214 if not self.options.name:
215 self.options.name = self.GetPatchName()
216 if not self.options.email:
217 self.options.email = scm.GIT.GetEmail('.')
218 186
219 187
220 def _ParseSendChangeOptions(options): 188 def _ParseSendChangeOptions(options):
221 """Parse common options passed to _SendChangeHTTP and _SendChangeSVN.""" 189 """Parse common options passed to _SendChangeHTTP and _SendChangeSVN."""
222 values = {} 190 values = {}
223 if options.email: 191 if options.email:
224 values['email'] = options.email 192 values['email'] = options.email
225 values['user'] = options.user 193 values['user'] = options.user
226 values['name'] = options.name 194 values['name'] = options.name
227 if options.bot: 195 if options.bot:
(...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after
358 """ 326 """
359 __pychecker__ = 'no-returnvalues' 327 __pychecker__ = 'no-returnvalues'
360 # Subversion has a .svn in all working directories. 328 # Subversion has a .svn in all working directories.
361 if os.path.isdir('.svn'): 329 if os.path.isdir('.svn'):
362 logging.info("Guessed VCS = Subversion") 330 logging.info("Guessed VCS = Subversion")
363 return SVN(options) 331 return SVN(options)
364 332
365 # Git has a command to test if you're in a git tree. 333 # Git has a command to test if you're in a git tree.
366 # Try running it, but don't die if we don't have git installed. 334 # Try running it, but don't die if we don't have git installed.
367 try: 335 try:
368 out, returncode = subprocess.Popen( 336 gclient_utils.CheckCall(["git", "rev-parse", "--is-inside-work-tree"])
369 ["git", "rev-parse", "--is-inside-work-tree"], 337 logging.info("Guessed VCS = Git")
370 shell=sys.platform.startswith('win'), 338 return GIT(options)
371 stdout=subprocess.PIPE).communicate()[0] 339 except gclient_utils.CheckCallError, e:
372 if returncode == 0: 340 if e.retcode != 2: # ENOENT -- they don't have git installed.
373 logging.info("Guessed VCS = Git")
374 return GIT(options)
375 except OSError, (errno, message):
376 if errno != 2: # ENOENT -- they don't have git installed.
377 raise 341 raise
378
379 raise NoTryServerAccess("Could not guess version control system. " 342 raise NoTryServerAccess("Could not guess version control system. "
380 "Are you in a working copy directory?") 343 "Are you in a working copy directory?")
381 344
382 345
383 def TryChange(argv, 346 def TryChange(argv,
384 file_list, 347 file_list,
385 swallow_exception, 348 swallow_exception,
386 prog=None): 349 prog=None):
387 """ 350 """
388 Args: 351 Args:
(...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after
513 476
514 try: 477 try:
515 # Convert options.diff into the content of the diff. 478 # Convert options.diff into the content of the diff.
516 if options.url: 479 if options.url:
517 options.diff = urllib.urlopen(options.url).read() 480 options.diff = urllib.urlopen(options.url).read()
518 elif options.diff: 481 elif options.diff:
519 options.diff = gclient_utils.FileRead(options.diff, 'rb') 482 options.diff = gclient_utils.FileRead(options.diff, 'rb')
520 # Process the VCS in any case at least to retrieve the email address. 483 # Process the VCS in any case at least to retrieve the email address.
521 try: 484 try:
522 options.scm = GuessVCS(options) 485 options.scm = GuessVCS(options)
523 options.scm.ProcessOptions()
524 except NoTryServerAccess, e: 486 except NoTryServerAccess, e:
525 # If we got the diff, we don't care. 487 # If we got the diff, we don't care.
526 if not options.diff: 488 if not options.diff:
527 # TODO(maruel): Raise what? 489 # TODO(maruel): Raise what?
528 raise 490 raise
529 491
530 # Get try slaves from PRESUBMIT.py files if not specified. 492 # Get try slaves from PRESUBMIT.py files if not specified.
531 if not options.bot: 493 if not options.bot:
532 if options.url: 494 if options.url:
533 parser.error('You need to specify which bots to use.') 495 parser.error('You need to specify which bots to use.')
(...skipping 23 matching lines...) Expand all
557 except (InvalidScript, NoTryServerAccess), e: 519 except (InvalidScript, NoTryServerAccess), e:
558 if swallow_exception: 520 if swallow_exception:
559 return 1 521 return 1
560 print e 522 print e
561 return 1 523 return 1
562 return 0 524 return 0
563 525
564 526
565 if __name__ == "__main__": 527 if __name__ == "__main__":
566 sys.exit(TryChange(None, [], False)) 528 sys.exit(TryChange(None, [], False))
OLDNEW
« gcl.py ('K') | « tests/trychange_unittest.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698