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

Side by Side Diff: tools/roll_deps.py

Issue 141483011: add_codereview_message script to append messages to a CL (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: lots of changes Created 6 years, 10 months 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
« no previous file with comments | « tools/misc_utils.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/python2 1 #!/usr/bin/python2
2 2
3 # Copyright 2014 Google Inc. 3 # Copyright 2014 Google Inc.
4 # 4 #
5 # Use of this source code is governed by a BSD-style license that can be 5 # Use of this source code is governed by a BSD-style license that can be
6 # found in the LICENSE file. 6 # found in the LICENSE file.
7 7
8 """Skia's Chromium DEPS roll script. 8 """Skia's Chromium DEPS roll script.
9 9
10 This script: 10 This script:
11 - searches through the last N Skia git commits to find out the hash that is 11 - searches through the last N Skia git commits to find out the hash that is
12 associated with the SVN revision number. 12 associated with the SVN revision number.
13 - creates a new branch in the Chromium tree, modifies the DEPS file to 13 - creates a new branch in the Chromium tree, modifies the DEPS file to
14 point at the given Skia commit, commits, uploads to Rietveld, and 14 point at the given Skia commit, commits, uploads to Rietveld, and
15 deletes the local copy of the branch. 15 deletes the local copy of the branch.
16 - creates a whitespace-only commit and uploads that to to Rietveld. 16 - creates a whitespace-only commit and uploads that to to Rietveld.
17 - returns the Chromium tree to its previous state. 17 - returns the Chromium tree to its previous state.
18 18
19 To specify the location of the git executable, set the GIT_EXECUTABLE
20 environment variable.
21
19 Usage: 22 Usage:
20 %prog -c CHROMIUM_PATH -r REVISION [OPTIONAL_OPTIONS] 23 %prog -c CHROMIUM_PATH -r REVISION [OPTIONAL_OPTIONS]
21 """ 24 """
22 25
23 26
24 import optparse 27 import optparse
25 import os 28 import os
26 import re 29 import re
27 import shutil 30 import shutil
28 import subprocess 31 import subprocess
29 import sys 32 import sys
30 import tempfile 33 import tempfile
31 34
35 import git_utils
36 import misc_utils
37
32 38
33 DEFAULT_BOTS_LIST = [ 39 DEFAULT_BOTS_LIST = [
34 'android_clang_dbg', 40 'android_clang_dbg',
35 'android_dbg', 41 'android_dbg',
36 'android_rel', 42 'android_rel',
37 'cros_daisy', 43 'cros_daisy',
38 'linux', 44 'linux',
39 'linux_asan', 45 'linux_asan',
40 'linux_chromeos', 46 'linux_chromeos',
41 'linux_chromeos_asan', 47 'linux_chromeos_asan',
(...skipping 25 matching lines...) Expand all
67 self.skip_cl_upload: (boolean) 73 self.skip_cl_upload: (boolean)
68 self.cl_bot_list: (list of strings) 74 self.cl_bot_list: (list of strings)
69 """ 75 """
70 76
71 # pylint: disable=I0011,R0903,R0902 77 # pylint: disable=I0011,R0903,R0902
72 def __init__(self, options=None): 78 def __init__(self, options=None):
73 self.skia_url = 'https://skia.googlesource.com/skia.git' 79 self.skia_url = 'https://skia.googlesource.com/skia.git'
74 self.revision_format = ( 80 self.revision_format = (
75 'git-svn-id: http://skia.googlecode.com/svn/trunk@%d ') 81 'git-svn-id: http://skia.googlecode.com/svn/trunk@%d ')
76 82
83 self.git = git_utils.git_executable()
84
77 if not options: 85 if not options:
78 options = DepsRollConfig.GetOptionParser() 86 options = DepsRollConfig.GetOptionParser()
79 # pylint: disable=I0011,E1103 87 # pylint: disable=I0011,E1103
80 self.verbose = options.verbose 88 self.verbose = options.verbose
81 self.vsp = VerboseSubprocess(self.verbose) 89 self.vsp = misc_utils.VerboseSubprocess(self.verbose)
82 self.save_branches = not options.delete_branches 90 self.save_branches = not options.delete_branches
83 self.search_depth = options.search_depth 91 self.search_depth = options.search_depth
84 self.chromium_path = options.chromium_path 92 self.chromium_path = options.chromium_path
85 self.git = options.git_path
86 self.skip_cl_upload = options.skip_cl_upload 93 self.skip_cl_upload = options.skip_cl_upload
87 # Split and remove empty strigns from the bot list. 94 # Split and remove empty strigns from the bot list.
88 self.cl_bot_list = [bot for bot in options.bots.split(',') if bot] 95 self.cl_bot_list = [bot for bot in options.bots.split(',') if bot]
89 self.skia_git_checkout_path = options.skia_git_path 96 self.skia_git_checkout_path = options.skia_git_path
90 self.default_branch_name = 'autogenerated_deps_roll_branch' 97 self.default_branch_name = 'autogenerated_deps_roll_branch'
91 self.reviewers_list = ','.join([ 98 self.reviewers_list = ','.join([
92 # 'rmistry@google.com', 99 # 'rmistry@google.com',
93 # 'reed@google.com', 100 # 'reed@google.com',
94 # 'bsalomon@google.com', 101 # 'bsalomon@google.com',
95 # 'robertphillips@google.com', 102 # 'robertphillips@google.com',
96 ]) 103 ])
97 self.cc_list = ','.join([ 104 self.cc_list = ','.join([
98 # 'skia-team@google.com', 105 # 'skia-team@google.com',
99 ]) 106 ])
100 107
101 @staticmethod 108 @staticmethod
102 def GetOptionParser(): 109 def GetOptionParser():
103 # pylint: disable=I0011,C0103 110 # pylint: disable=I0011,C0103
104 """Returns an optparse.OptionParser object. 111 """Returns an optparse.OptionParser object.
105 112
106 Returns: 113 Returns:
107 An optparse.OptionParser object. 114 An optparse.OptionParser object.
(...skipping 20 matching lines...) Expand all
128 option_parser.add_option( 135 option_parser.add_option(
129 '', '--skia_git_path', 136 '', '--skia_git_path',
130 help='Path of a pure-git Skia repository checkout. If empty,' 137 help='Path of a pure-git Skia repository checkout. If empty,'
131 ' a temporary will be cloned. Defaults to SKIA_GIT_CHECKOUT' 138 ' a temporary will be cloned. Defaults to SKIA_GIT_CHECKOUT'
132 '_PATH, if that environment variable is set.', 139 '_PATH, if that environment variable is set.',
133 default=os.environ.get('SKIA_GIT_CHECKOUT_PATH')) 140 default=os.environ.get('SKIA_GIT_CHECKOUT_PATH'))
134 option_parser.add_option( 141 option_parser.add_option(
135 '', '--search_depth', type='int', default=100, 142 '', '--search_depth', type='int', default=100,
136 help='How far back to look for the revision.') 143 help='How far back to look for the revision.')
137 option_parser.add_option( 144 option_parser.add_option(
138 '', '--git_path', help='Git executable, defaults to "git".',
139 default='git')
140 option_parser.add_option(
141 '', '--delete_branches', help='Delete the temporary branches', 145 '', '--delete_branches', help='Delete the temporary branches',
142 action='store_true', dest='delete_branches', default=False) 146 action='store_true', dest='delete_branches', default=False)
143 option_parser.add_option( 147 option_parser.add_option(
144 '', '--verbose', help='Do not suppress the output from `git cl`.', 148 '', '--verbose', help='Do not suppress the output from `git cl`.',
145 action='store_true', dest='verbose', default=False) 149 action='store_true', dest='verbose', default=False)
146 option_parser.add_option( 150 option_parser.add_option(
147 '', '--skip_cl_upload', help='Skip the cl upload step; useful' 151 '', '--skip_cl_upload', help='Skip the cl upload step; useful'
148 ' for testing or with --save_branches.', 152 ' for testing.',
149 action='store_true', default=False) 153 action='store_true', default=False)
150 154
151 default_bots_help = ( 155 default_bots_help = (
152 'Comma-separated list of bots, defaults to a list of %d bots.' 156 'Comma-separated list of bots, defaults to a list of %d bots.'
153 ' To skip `git cl try`, set this to an empty string.' 157 ' To skip `git cl try`, set this to an empty string.'
154 % len(DEFAULT_BOTS_LIST)) 158 % len(DEFAULT_BOTS_LIST))
155 default_bots = ','.join(DEFAULT_BOTS_LIST) 159 default_bots = ','.join(DEFAULT_BOTS_LIST)
156 option_parser.add_option( 160 option_parser.add_option(
157 '', '--bots', help=default_bots_help, default=default_bots) 161 '', '--bots', help=default_bots_help, default=default_bots)
158 162
159 return option_parser 163 return option_parser
160 164
161 165
162 def test_git_executable(git_executable):
163 """Test the git executable.
164
165 Args:
166 git_executable: git executable path.
167 Returns:
168 True if test is successful.
169 """
170 with open(os.devnull, 'w') as devnull:
171 try:
172 subprocess.call([git_executable, '--version'], stdout=devnull)
173 except (OSError,):
174 return False
175 return True
176
177
178 class DepsRollError(Exception): 166 class DepsRollError(Exception):
179 """Exceptions specific to this module.""" 167 """Exceptions specific to this module."""
180 pass 168 pass
181 169
182 170
183 class VerboseSubprocess(object):
184 """Call subprocess methods, but print out command before executing.
185
186 Attributes:
187 verbose: (boolean) should we print out the command or not. If
188 not, this is the same as calling the subprocess method
189 quiet: (boolean) suppress stdout on check_call and call.
190 prefix: (string) When verbose, what to print before each command.
191 """
192
193 def __init__(self, verbose):
194 self.verbose = verbose
195 self.quiet = not verbose
196 self.prefix = '~~$ '
197
198 @staticmethod
199 def _fix(string):
200 """Quote and escape a string if necessary."""
201 if ' ' in string or '\n' in string:
202 string = '"%s"' % string.replace('\n', '\\n')
203 return string
204
205 @staticmethod
206 def print_subprocess_args(prefix, *args, **kwargs):
207 """Print out args in a human-readable manner."""
208 if 'cwd' in kwargs:
209 print '%scd %s' % (prefix, kwargs['cwd'])
210 print prefix + ' '.join(VerboseSubprocess._fix(arg) for arg in args[0])
211 if 'cwd' in kwargs:
212 print '%scd -' % prefix
213
214 def check_call(self, *args, **kwargs):
215 """Wrapper for subprocess.check_call().
216
217 Args:
218 *args: to be passed to subprocess.check_call()
219 **kwargs: to be passed to subprocess.check_call()
220 Returns:
221 Whatever subprocess.check_call() returns.
222 Raises:
223 OSError or subprocess.CalledProcessError: raised by check_call.
224 """
225 if self.verbose:
226 self.print_subprocess_args(self.prefix, *args, **kwargs)
227 if self.quiet:
228 with open(os.devnull, 'w') as devnull:
229 return subprocess.check_call(*args, stdout=devnull, **kwargs)
230 else:
231 return subprocess.check_call(*args, **kwargs)
232
233 def call(self, *args, **kwargs):
234 """Wrapper for subprocess.check().
235
236 Args:
237 *args: to be passed to subprocess.check_call()
238 **kwargs: to be passed to subprocess.check_call()
239 Returns:
240 Whatever subprocess.call() returns.
241 Raises:
242 OSError or subprocess.CalledProcessError: raised by call.
243 """
244 if self.verbose:
245 self.print_subprocess_args(self.prefix, *args, **kwargs)
246 if self.quiet:
247 with open(os.devnull, 'w') as devnull:
248 return subprocess.call(*args, stdout=devnull, **kwargs)
249 else:
250 return subprocess.call(*args, **kwargs)
251
252 def check_output(self, *args, **kwargs):
253 """Wrapper for subprocess.check_output().
254
255 Args:
256 *args: to be passed to subprocess.check_output()
257 **kwargs: to be passed to subprocess.check_output()
258 Returns:
259 Whatever subprocess.check_output() returns.
260 Raises:
261 OSError or subprocess.CalledProcessError: raised by check_output.
262 """
263 if self.verbose:
264 self.print_subprocess_args(self.prefix, *args, **kwargs)
265 return subprocess.check_output(*args, **kwargs)
266
267 def strip_output(self, *args, **kwargs):
268 """Wrap subprocess.check_output and str.strip().
269
270 Pass the given arguments into subprocess.check_output() and return
271 the results, after stripping any excess whitespace.
272
273 Args:
274 *args: to be passed to subprocess.check_output()
275 **kwargs: to be passed to subprocess.check_output()
276
277 Returns:
278 The output of the process as a string without leading or
279 trailing whitespace.
280 Raises:
281 OSError or subprocess.CalledProcessError: raised by check_output.
282 """
283 if self.verbose:
284 self.print_subprocess_args(self.prefix, *args, **kwargs)
285 return str(subprocess.check_output(*args, **kwargs)).strip()
286
287 def popen(self, *args, **kwargs):
288 """Wrapper for subprocess.Popen().
289
290 Args:
291 *args: to be passed to subprocess.Popen()
292 **kwargs: to be passed to subprocess.Popen()
293 Returns:
294 The output of subprocess.Popen()
295 Raises:
296 OSError or subprocess.CalledProcessError: raised by Popen.
297 """
298 if self.verbose:
299 self.print_subprocess_args(self.prefix, *args, **kwargs)
300 return subprocess.Popen(*args, **kwargs)
301
302
303 class ChangeDir(object):
304 """Use with a with-statement to temporarily change directories."""
305 # pylint: disable=I0011,R0903
306
307 def __init__(self, directory, verbose=False):
308 self._directory = directory
309 self._verbose = verbose
310
311 def __enter__(self):
312 if self._verbose:
313 print '~~$ cd %s' % self._directory
314 cwd = os.getcwd()
315 os.chdir(self._directory)
316 self._directory = cwd
317
318 def __exit__(self, etype, value, traceback):
319 if self._verbose:
320 print '~~$ cd %s' % self._directory
321 os.chdir(self._directory)
322
323
324 class ReSearch(object):
325 """A collection of static methods for regexing things."""
326
327 @staticmethod
328 def search_within_stream(input_stream, pattern, default=None):
329 """Search for regular expression in a file-like object.
330
331 Opens a file for reading and searches line by line for a match to
332 the regex and returns the parenthesized group named return for the
333 first match. Does not search across newlines.
334
335 For example:
336 pattern = '^root(:[^:]*){4}:(?P<return>[^:]*)'
337 with open('/etc/passwd', 'r') as stream:
338 return search_within_file(stream, pattern)
339 should return root's home directory (/root on my system).
340
341 Args:
342 input_stream: file-like object to be read
343 pattern: (string) to be passed to re.compile
344 default: what to return if no match
345
346 Returns:
347 A string or whatever default is
348 """
349 pattern_object = re.compile(pattern)
350 for line in input_stream:
351 match = pattern_object.search(line)
352 if match:
353 return match.group('return')
354 return default
355
356 @staticmethod
357 def search_within_string(input_string, pattern, default=None):
358 """Search for regular expression in a string.
359
360 Args:
361 input_string: (string) to be searched
362 pattern: (string) to be passed to re.compile
363 default: what to return if no match
364
365 Returns:
366 A string or whatever default is
367 """
368 match = re.search(pattern, input_string)
369 return match.group('return') if match else default
370
371 @staticmethod
372 def search_within_output(verbose, pattern, default, *args, **kwargs):
373 """Search for regular expression in a process output.
374
375 Does not search across newlines.
376
377 Args:
378 verbose: (boolean) shoule we call
379 VerboseSubprocess.print_subprocess_args?
380 pattern: (string) to be passed to re.compile
381 default: what to return if no match
382 *args: to be passed to subprocess.Popen()
383 **kwargs: to be passed to subprocess.Popen()
384
385 Returns:
386 A string or whatever default is
387 """
388 if verbose:
389 VerboseSubprocess.print_subprocess_args(
390 '~~$ ', *args, **kwargs)
391 proc = subprocess.Popen(*args, stdout=subprocess.PIPE, **kwargs)
392 return ReSearch.search_within_stream(proc.stdout, pattern, default)
393
394
395 def get_svn_revision(config, commit): 171 def get_svn_revision(config, commit):
396 """Works in both git and git-svn. returns a string.""" 172 """Works in both git and git-svn. returns a string."""
397 svn_format = ( 173 svn_format = (
398 '(git-svn-id: [^@ ]+@|SVN changes up to revision |' 174 '(git-svn-id: [^@ ]+@|SVN changes up to revision |'
399 'LKGR w/ DEPS up to revision )(?P<return>[0-9]+)') 175 'LKGR w/ DEPS up to revision )(?P<return>[0-9]+)')
400 svn_revision = ReSearch.search_within_output( 176 svn_revision = misc_utils.ReSearch.search_within_output(
401 config.verbose, svn_format, None, 177 config.verbose, svn_format, None,
402 [config.git, 'log', '-n', '1', '--format=format:%B', commit]) 178 [config.git, 'log', '-n', '1', '--format=format:%B', commit])
403 if not svn_revision: 179 if not svn_revision:
404 raise DepsRollError( 180 raise DepsRollError(
405 'Revision number missing from Chromium origin/master.') 181 'Revision number missing from Chromium origin/master.')
406 return int(svn_revision) 182 return int(svn_revision)
407 183
408 184
409 class SkiaGitCheckout(object): 185 class SkiaGitCheckout(object):
410 """Class to create a temporary skia git checkout, if necessary. 186 """Class to create a temporary skia git checkout, if necessary.
411 """ 187 """
412 # pylint: disable=I0011,R0903 188 # pylint: disable=I0011,R0903
413 189
414 def __init__(self, config, depth): 190 def __init__(self, config, depth):
415 self._config = config 191 self._config = config
416 self._depth = depth 192 self._depth = depth
417 self._use_temp = None 193 self._use_temp = None
418 self._original_cwd = None 194 self._original_cwd = None
419 195
420 def __enter__(self): 196 def __enter__(self):
421 config = self._config 197 config = self._config
422 git = config.git 198 git = config.git
423 skia_dir = None 199 skia_dir = None
424 self._original_cwd = os.getcwd() 200 self._original_cwd = os.getcwd()
425 if config.skia_git_checkout_path: 201 if config.skia_git_checkout_path:
426 skia_dir = config.skia_git_checkout_path 202 if config.skia_git_checkout_path != os.curdir:
427 ## Update origin/master if needed. 203 skia_dir = config.skia_git_checkout_path
428 if self._config.verbose: 204 ## Update origin/master if needed.
429 print '~~$', 'cd', skia_dir 205 if self._config.verbose:
430 os.chdir(skia_dir) 206 print '~~$', 'cd', skia_dir
207 os.chdir(skia_dir)
431 config.vsp.check_call([git, 'fetch', '-q', 'origin']) 208 config.vsp.check_call([git, 'fetch', '-q', 'origin'])
432 self._use_temp = None 209 self._use_temp = None
433 else: 210 else:
434 skia_dir = tempfile.mkdtemp(prefix='git_skia_tmp_') 211 skia_dir = tempfile.mkdtemp(prefix='git_skia_tmp_')
435 self._use_temp = skia_dir 212 self._use_temp = skia_dir
436 try: 213 try:
437 os.chdir(skia_dir) 214 os.chdir(skia_dir)
438 config.vsp.check_call( 215 config.vsp.check_call(
439 [git, 'clone', '-q', '--depth=%d' % self._depth, 216 [git, 'clone', '-q', '--depth=%d' % self._depth,
440 '--single-branch', config.skia_url, '.']) 217 '--single-branch', config.skia_url, '.'])
441 except (OSError, subprocess.CalledProcessError) as error: 218 except (OSError, subprocess.CalledProcessError) as error:
442 shutil.rmtree(skia_dir) 219 shutil.rmtree(skia_dir)
443 raise error 220 raise error
444 221
445 def __exit__(self, etype, value, traceback): 222 def __exit__(self, etype, value, traceback):
446 if self._config.verbose: 223 if self._config.skia_git_checkout_path != os.curdir:
447 print '~~$', 'cd', self._original_cwd 224 if self._config.verbose:
448 os.chdir(self._original_cwd) 225 print '~~$', 'cd', self._original_cwd
226 os.chdir(self._original_cwd)
449 if self._use_temp: 227 if self._use_temp:
450 shutil.rmtree(self._use_temp) 228 shutil.rmtree(self._use_temp)
451 229
452 230
453 def revision_and_hash(config): 231 def revision_and_hash(config):
454 """Finds revision number and git hash of origin/master in the Skia tree. 232 """Finds revision number and git hash of origin/master in the Skia tree.
455 233
456 Args: 234 Args:
457 config: (roll_deps.DepsRollConfig) object containing options. 235 config: (roll_deps.DepsRollConfig) object containing options.
458 236
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after
521 """ 299 """
522 with SkiaGitCheckout(config, config.search_depth): 300 with SkiaGitCheckout(config, config.search_depth):
523 git_hash = config.vsp.strip_output( 301 git_hash = config.vsp.strip_output(
524 ['git', 'log', '-n', '1', '--format=format:%H', partial_hash]) 302 ['git', 'log', '-n', '1', '--format=format:%H', partial_hash])
525 if not git_hash: 303 if not git_hash:
526 raise DepsRollError('Partial Git hash can not be found.') 304 raise DepsRollError('Partial Git hash can not be found.')
527 revision = get_svn_revision(config, git_hash) 305 revision = get_svn_revision(config, git_hash)
528 return revision, git_hash 306 return revision, git_hash
529 307
530 308
531 class GitBranchCLUpload(object):
532 """Class to manage git branches and git-cl-upload.
533
534 This class allows one to create a new branch in a repository based
535 off of origin/master, make changes to the tree inside the
536 with-block, upload that new branch to Rietveld, restore the original
537 tree state, and delete the local copy of the new branch.
538
539 See roll_deps() for an example of use.
540
541 Constructor Args:
542 config: (roll_deps.DepsRollConfig) object containing options.
543 message: (string) the commit message, can be multiline.
544 set_brach_name: (string or none) if not None, the name of the
545 branch to use. If None, then use a temporary branch that
546 will be deleted.
547
548 Attributes:
549 issue: a string describing the codereview issue, after __exit__
550 has been called, othrwise, None.
551
552 Raises:
553 OSError: failed to execute git or git-cl.
554 subprocess.CalledProcessError: git returned unexpected status.
555 """
556 # pylint: disable=I0011,R0903,R0902
557
558 def __init__(self, config, message, set_branch_name):
559 self._message = message
560 self._file_list = []
561 self._branch_name = set_branch_name
562 self._stash = None
563 self._original_branch = None
564 self._config = config
565 self.issue = None
566
567 def stage_for_commit(self, *paths):
568 """Calls `git add ...` on each argument.
569
570 Args:
571 *paths: (list of strings) list of filenames to pass to `git add`.
572 """
573 self._file_list.extend(paths)
574
575 def __enter__(self):
576 git = self._config.git
577 vsp = self._config.vsp
578 def branch_exists(branch):
579 """Return true iff branch exists."""
580 return 0 == vsp.call([git, 'show-ref', '--quiet', branch])
581 def has_diff():
582 """Return true iff repository has uncommited changes."""
583 return bool(vsp.call([git, 'diff', '--quiet', 'HEAD']))
584
585 self._stash = has_diff()
586 if self._stash:
587 vsp.check_call([git, 'stash', 'save'])
588 try:
589 full_branch = vsp.strip_output([git, 'symbolic-ref', 'HEAD'])
590 self._original_branch = full_branch.split('/')[-1]
591 except (subprocess.CalledProcessError,):
592 self._original_branch = vsp.strip_output(
593 [git, 'rev-parse', 'HEAD'])
594
595 if not self._branch_name:
596 self._branch_name = self._config.default_branch_name
597
598 if branch_exists(self._branch_name):
599 vsp.check_call([git, 'checkout', '-q', 'master'])
600 vsp.check_call([git, 'branch', '-D', self._branch_name])
601
602 vsp.check_call(
603 [git, 'checkout', '-q', '-b', self._branch_name, 'origin/master'])
604
605 def __exit__(self, etype, value, traceback):
606 # pylint: disable=I0011,R0912
607 git = self._config.git
608 vsp = self._config.vsp
609 svn_info = str(get_svn_revision(self._config, 'HEAD'))
610
611 for filename in self._file_list:
612 assert os.path.exists(filename)
613 vsp.check_call([git, 'add', filename])
614 vsp.check_call([git, 'commit', '-q', '-m', self._message])
615
616 git_cl = [git, 'cl', 'upload', '-f',
617 '--bypass-hooks', '--bypass-watchlists']
618 if self._config.cc_list:
619 git_cl.append('--cc=%s' % self._config.cc_list)
620 if self._config.reviewers_list:
621 git_cl.append('--reviewers=%s' % self._config.reviewers_list)
622
623 git_try = [git, 'cl', 'try', '--revision', svn_info]
624 git_try.extend([arg for bot in self._config.cl_bot_list
625 for arg in ('-b', bot)])
626
627 if self._config.skip_cl_upload:
628 print 'You should call:'
629 print ' cd %s' % os.getcwd()
630 VerboseSubprocess.print_subprocess_args(
631 ' ', [git, 'checkout', self._branch_name])
632 VerboseSubprocess.print_subprocess_args(' ', git_cl)
633 if self._config.cl_bot_list:
634 VerboseSubprocess.print_subprocess_args(' ', git_try)
635 print
636 self.issue = ''
637 else:
638 vsp.check_call(git_cl)
639 self.issue = vsp.strip_output([git, 'cl', 'issue'])
640 if self._config.cl_bot_list:
641 vsp.check_call(git_try)
642
643 # deal with the aftermath of failed executions of this script.
644 if self._config.default_branch_name == self._original_branch:
645 self._original_branch = 'master'
646 vsp.check_call([git, 'checkout', '-q', self._original_branch])
647
648 if self._config.default_branch_name == self._branch_name:
649 vsp.check_call([git, 'branch', '-D', self._branch_name])
650 if self._stash:
651 vsp.check_call([git, 'stash', 'pop'])
652
653
654 def change_skia_deps(revision, git_hash, depspath): 309 def change_skia_deps(revision, git_hash, depspath):
655 """Update the DEPS file. 310 """Update the DEPS file.
656 311
657 Modify the skia_revision and skia_hash entries in the given DEPS file. 312 Modify the skia_revision and skia_hash entries in the given DEPS file.
658 313
659 Args: 314 Args:
660 revision: (int) Skia SVN revision. 315 revision: (int) Skia SVN revision.
661 git_hash: (string) Skia Git hash. 316 git_hash: (string) Skia Git hash.
662 depspath: (string) path to DEPS file. 317 depspath: (string) path to DEPS file.
663 """ 318 """
664 temp_file = tempfile.NamedTemporaryFile(delete=False, 319 temp_file = tempfile.NamedTemporaryFile(delete=False,
665 prefix='skia_DEPS_ROLL_tmp_') 320 prefix='skia_DEPS_ROLL_tmp_')
666 try: 321 try:
667 deps_regex_rev = re.compile('"skia_revision": "[0-9]*",') 322 deps_regex_rev = re.compile('"skia_revision": "[0-9]*",')
668 deps_regex_hash = re.compile('"skia_hash": "[0-9a-f]*",') 323 deps_regex_hash = re.compile('"skia_hash": "[0-9a-f]*",')
669 324
670 deps_regex_rev_repl = '"skia_revision": "%d",' % revision 325 deps_regex_rev_repl = '"skia_revision": "%d",' % revision
671 deps_regex_hash_repl = '"skia_hash": "%s",' % git_hash 326 deps_regex_hash_repl = '"skia_hash": "%s",' % git_hash
672 327
673 with open(depspath, 'r') as input_stream: 328 with open(depspath, 'r') as input_stream:
674 for line in input_stream: 329 for line in input_stream:
675 line = deps_regex_rev.sub(deps_regex_rev_repl, line) 330 line = deps_regex_rev.sub(deps_regex_rev_repl, line)
676 line = deps_regex_hash.sub(deps_regex_hash_repl, line) 331 line = deps_regex_hash.sub(deps_regex_hash_repl, line)
677 temp_file.write(line) 332 temp_file.write(line)
678 finally: 333 finally:
679 temp_file.close() 334 temp_file.close()
680 shutil.move(temp_file.name, depspath) 335 shutil.move(temp_file.name, depspath)
681 336
682 337
338 def git_cl_uploader(config, message, file_list):
339 """Create a commit in the current git branch; upload via git-cl.
340
341 Assumes that you are already on the branch you want to be on.
342
343 Args:
344 config: (roll_deps.DepsRollConfig) object containing options.
345 message: (string) the commit message, can be multiline.
346 file_list: (list of strings) list of filenames to pass to `git add`.
347
348 Returns:
349 The output of `git cl issue`, if not config.skip_cl_upload, else ''.
350 """
351
352 git, vsp = config.git, config.vsp
353 svn_info = str(get_svn_revision(config, 'HEAD'))
354
355 for filename in file_list:
356 assert os.path.exists(filename)
357 vsp.check_call([git, 'add', filename])
358
359 vsp.check_call([git, 'commit', '-q', '-m', message])
360
361 git_cl = [git, 'cl', 'upload', '-f',
362 '--bypass-hooks', '--bypass-watchlists']
363 if config.cc_list:
364 git_cl.append('--cc=%s' % config.cc_list)
365 if config.reviewers_list:
366 git_cl.append('--reviewers=%s' % config.reviewers_list)
367
368 git_try = [git, 'cl', 'try', '--revision', svn_info]
369 git_try.extend([arg for bot in config.cl_bot_list for arg in ('-b', bot)])
370
371 branch_name = git_utils.git_branch_name(vsp.verbose)
372
373 if config.skip_cl_upload:
374 space = ' '
375 print 'You should call:'
376 print '%scd %s' % (space, os.getcwd())
377 misc_utils.print_subprocess_args(space, [git, 'checkout', branch_name])
378 misc_utils.print_subprocess_args(space, git_cl)
379 if config.cl_bot_list:
380 misc_utils.print_subprocess_args(space, git_try)
381 print
382 return ''
383 else:
384 vsp.check_call(git_cl)
385 issue = vsp.strip_output([git, 'cl', 'issue'])
386 if config.cl_bot_list:
387 vsp.check_call(git_try)
388 return issue
389
390
683 def roll_deps(config, revision, git_hash): 391 def roll_deps(config, revision, git_hash):
684 """Upload changed DEPS and a whitespace change. 392 """Upload changed DEPS and a whitespace change.
685 393
686 Given the correct git_hash, create two Reitveld issues. 394 Given the correct git_hash, create two Reitveld issues.
687 395
688 Args: 396 Args:
689 config: (roll_deps.DepsRollConfig) object containing options. 397 config: (roll_deps.DepsRollConfig) object containing options.
690 revision: (int) Skia SVN revision. 398 revision: (int) Skia SVN revision.
691 git_hash: (string) Skia Git hash. 399 git_hash: (string) Skia Git hash.
692 400
693 Returns: 401 Returns:
694 a tuple containing textual description of the two issues. 402 a tuple containing textual description of the two issues.
695 403
696 Raises: 404 Raises:
697 OSError: failed to execute git or git-cl. 405 OSError: failed to execute git or git-cl.
698 subprocess.CalledProcessError: git returned unexpected status. 406 subprocess.CalledProcessError: git returned unexpected status.
699 """ 407 """
700 408
701 git = config.git 409 git = config.git
702 with ChangeDir(config.chromium_path, config.verbose): 410 with misc_utils.ChangeDir(config.chromium_path, config.verbose):
703 config.vsp.check_call([git, 'fetch', '-q', 'origin']) 411 config.vsp.check_call([git, 'fetch', '-q', 'origin'])
704 412
705 old_revision = ReSearch.search_within_output( 413 old_revision = misc_utils.ReSearch.search_within_output(
706 config.verbose, '"skia_revision": "(?P<return>[0-9]+)",', None, 414 config.verbose, '"skia_revision": "(?P<return>[0-9]+)",', None,
707 [git, 'show', 'origin/master:DEPS']) 415 [git, 'show', 'origin/master:DEPS'])
708 assert old_revision 416 assert old_revision
709 if revision == int(old_revision): 417 if revision == int(old_revision):
710 print 'DEPS is up to date!' 418 print 'DEPS is up to date!'
711 return None 419 return None
712 420
713 master_hash = config.vsp.strip_output( 421 master_hash = config.vsp.strip_output(
714 [git, 'show-ref', 'origin/master', '--hash']) 422 [git, 'show-ref', 'origin/master', '--hash'])
715 master_revision = get_svn_revision(config, 'origin/master') 423 master_revision = get_svn_revision(config, 'origin/master')
716 424
717 branch = None
718
719 # master_hash[8] gives each whitespace CL a unique name. 425 # master_hash[8] gives each whitespace CL a unique name.
426 if config.save_branches:
427 branch = 'control_%s' % master_hash[:8]
428 else:
429 branch = None
720 message = ('whitespace change %s\n\n' 430 message = ('whitespace change %s\n\n'
721 'Chromium base revision: %d / %s\n\n' 431 'Chromium base revision: %d / %s\n\n'
722 'This CL was created by Skia\'s roll_deps.py script.\n' 432 'This CL was created by Skia\'s roll_deps.py script.\n'
723 ) % (master_hash[:8], master_revision, master_hash[:8]) 433 ) % (master_hash[:8], master_revision, master_hash[:8])
724 if config.save_branches: 434 with git_utils.ChangeGitBranch(branch, 'origin/master',
725 branch = 'control_%s' % master_hash[:8] 435 config.verbose):
436 branch = git_utils.git_branch_name(config.vsp.verbose)
726 437
727 codereview = GitBranchCLUpload(config, message, branch)
728 with codereview:
729 with open('build/whitespace_file.txt', 'a') as output_stream: 438 with open('build/whitespace_file.txt', 'a') as output_stream:
730 output_stream.write('\nCONTROL\n') 439 output_stream.write('\nCONTROL\n')
731 codereview.stage_for_commit('build/whitespace_file.txt')
732 whitespace_cl = codereview.issue
733 if branch:
734 whitespace_cl = '%s\n branch: %s' % (whitespace_cl, branch)
735 440
736 control_url = ReSearch.search_within_string( 441 whitespace_cl = git_cl_uploader(
737 codereview.issue, '(?P<return>https?://[^) ]+)', '?') 442 config, message, ['build/whitespace_file.txt'])
443
444 control_url = misc_utils.ReSearch.search_within_string(
445 whitespace_cl, '(?P<return>https?://[^) ]+)', '?')
446 if config.save_branches:
447 whitespace_cl = '%s\n branch: %s' % (whitespace_cl, branch)
738 448
739 if config.save_branches: 449 if config.save_branches:
740 branch = 'roll_%d_%s' % (revision, master_hash[:8]) 450 branch = 'roll_%d_%s' % (revision, master_hash[:8])
451 else:
452 branch = None
741 message = ( 453 message = (
742 'roll skia DEPS to %d\n\n' 454 'roll skia DEPS to %d\n\n'
743 'Chromium base revision: %d / %s\n' 455 'Chromium base revision: %d / %s\n'
744 'Old Skia revision: %s\n' 456 'Old Skia revision: %s\n'
745 'New Skia revision: %d\n' 457 'New Skia revision: %d\n'
746 'Control CL: %s\n\n' 458 'Control CL: %s\n\n'
747 'This CL was created by Skia\'s roll_deps.py script.\n\n' 459 'This CL was created by Skia\'s roll_deps.py script.\n\n'
748 'Bypassing commit queue trybots:\n' 460 'Bypassing commit queue trybots:\n'
749 'NOTRY=true\n' 461 'NOTRY=true\n'
750 % (revision, master_revision, master_hash[:8], 462 % (revision, master_revision, master_hash[:8],
751 old_revision, revision, control_url)) 463 old_revision, revision, control_url))
752 codereview = GitBranchCLUpload(config, message, branch) 464 with git_utils.ChangeGitBranch(branch, 'origin/master',
753 with codereview: 465 config.verbose):
466 branch = git_utils.git_branch_name(config.vsp.verbose)
467
754 change_skia_deps(revision, git_hash, 'DEPS') 468 change_skia_deps(revision, git_hash, 'DEPS')
755 codereview.stage_for_commit('DEPS') 469 deps_cl = git_cl_uploader(config, message, ['DEPS'])
756 deps_cl = codereview.issue 470 if config.save_branches:
757 if branch: 471 deps_cl = '%s\n branch: %s' % (deps_cl, branch)
758 deps_cl = '%s\n branch: %s' % (deps_cl, branch)
759 472
760 return deps_cl, whitespace_cl 473 return deps_cl, whitespace_cl
761 474
762 475
763 def find_hash_and_roll_deps(config, revision=None, partial_hash=None): 476 def find_hash_and_roll_deps(config, revision=None, partial_hash=None):
764 """Call find_hash_from_revision() and roll_deps(). 477 """Call find_hash_from_revision() and roll_deps().
765 478
766 The calls to git will be verbose on standard output. After a 479 The calls to git will be verbose on standard output. After a
767 successful upload of both issues, print links to the new 480 successful upload of both issues, print links to the new
768 codereview issues. 481 codereview issues.
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
807 Args: 520 Args:
808 args: sys.argv[1:]-type argument list. 521 args: sys.argv[1:]-type argument list.
809 """ 522 """
810 option_parser = DepsRollConfig.GetOptionParser() 523 option_parser = DepsRollConfig.GetOptionParser()
811 options = option_parser.parse_args(args)[0] 524 options = option_parser.parse_args(args)[0]
812 525
813 if not options.chromium_path: 526 if not options.chromium_path:
814 option_parser.error('Must specify chromium_path.') 527 option_parser.error('Must specify chromium_path.')
815 if not os.path.isdir(options.chromium_path): 528 if not os.path.isdir(options.chromium_path):
816 option_parser.error('chromium_path must be a directory.') 529 option_parser.error('chromium_path must be a directory.')
817 if not test_git_executable(options.git_path): 530
531 if not git_utils.git_executable():
818 option_parser.error('Invalid git executable.') 532 option_parser.error('Invalid git executable.')
819 533
820 config = DepsRollConfig(options) 534 config = DepsRollConfig(options)
821 find_hash_and_roll_deps(config, options.revision, options.git_hash) 535 find_hash_and_roll_deps(config, options.revision, options.git_hash)
822 536
823 537
824 if __name__ == '__main__': 538 if __name__ == '__main__':
825 main(sys.argv[1:]) 539 main(sys.argv[1:])
826 540
OLDNEW
« no previous file with comments | « tools/misc_utils.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698