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

Side by Side Diff: checkout.py

Issue 11358165: Enforce 15 minutes timeout for all operations and 30 minutes for checkouts. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/depot_tools
Patch Set: Created 8 years, 1 month 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 | Annotate | Revision Log
« no previous file with comments | « no previous file | 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 # coding=utf8 1 # coding=utf8
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 # Copyright (c) 2012 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 """Manages a project checkout. 5 """Manages a project checkout.
6 6
7 Includes support for svn, git-svn and git. 7 Includes support for svn, git-svn and git.
8 """ 8 """
9 9
10 import ConfigParser 10 import ConfigParser
11 import fnmatch 11 import fnmatch
12 import logging 12 import logging
13 import os 13 import os
14 import re 14 import re
15 import shutil 15 import shutil
16 import subprocess 16 import subprocess
17 import sys 17 import sys
18 import tempfile 18 import tempfile
19 19
20 import patch 20 import patch
21 import scm 21 import scm
22 import subprocess2 22 import subprocess2
23 23
24 24
25 # Default timeout of 15 minutes.
26 GLOBAL_TIMEOUT = 15*60
27 # Use a larger timeout for checkout since it can be a genuinely slower
28 # operation.
29 FETCH_TIMEOUT = 30*60
30
31
25 def get_code_review_setting(path, key, 32 def get_code_review_setting(path, key,
26 codereview_settings_file='codereview.settings'): 33 codereview_settings_file='codereview.settings'):
27 """Parses codereview.settings and return the value for the key if present. 34 """Parses codereview.settings and return the value for the key if present.
28 35
29 Don't cache the values in case the file is changed.""" 36 Don't cache the values in case the file is changed."""
30 # TODO(maruel): Do not duplicate code. 37 # TODO(maruel): Do not duplicate code.
31 settings = {} 38 settings = {}
32 try: 39 try:
33 settings_file = open(os.path.join(path, codereview_settings_file), 'r') 40 settings_file = open(os.path.join(path, codereview_settings_file), 'r')
34 try: 41 try:
(...skipping 155 matching lines...) Expand 10 before | Expand all | Expand 10 after
190 stdout.append('Copied %s -> %s' % (p.source_filename, p.filename)) 197 stdout.append('Copied %s -> %s' % (p.source_filename, p.filename))
191 if p.diff_hunks: 198 if p.diff_hunks:
192 cmd = ['patch', '-u', '--binary', '-p%s' % p.patchlevel] 199 cmd = ['patch', '-u', '--binary', '-p%s' % p.patchlevel]
193 if verbose: 200 if verbose:
194 cmd.append('--verbose') 201 cmd.append('--verbose')
195 stdout.append( 202 stdout.append(
196 subprocess2.check_output( 203 subprocess2.check_output(
197 cmd, 204 cmd,
198 stdin=p.get(False), 205 stdin=p.get(False),
199 stderr=subprocess2.STDOUT, 206 stderr=subprocess2.STDOUT,
200 cwd=self.project_path)) 207 cwd=self.project_path,
208 timeout=GLOBAL_TIMEOUT))
201 elif p.is_new and not os.path.exists(filepath): 209 elif p.is_new and not os.path.exists(filepath):
202 # There is only a header. Just create the file. 210 # There is only a header. Just create the file.
203 open(filepath, 'w').close() 211 open(filepath, 'w').close()
204 stdout.append('Created an empty file.') 212 stdout.append('Created an empty file.')
205 for post in post_processors: 213 for post in post_processors:
206 post(self, p) 214 post(self, p)
207 if verbose: 215 if verbose:
208 print p.filename 216 print p.filename
209 print align_stdout(stdout) 217 print align_stdout(stdout)
210 except OSError, e: 218 except OSError, e:
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after
269 if self.commit_user: 277 if self.commit_user:
270 args.extend(['--username', self.commit_user]) 278 args.extend(['--username', self.commit_user])
271 if self.commit_pwd: 279 if self.commit_pwd:
272 args.extend(['--password', self.commit_pwd]) 280 args.extend(['--password', self.commit_pwd])
273 return args 281 return args
274 282
275 def _check_call_svn(self, args, **kwargs): 283 def _check_call_svn(self, args, **kwargs):
276 """Runs svn and throws an exception if the command failed.""" 284 """Runs svn and throws an exception if the command failed."""
277 kwargs.setdefault('cwd', self.project_path) 285 kwargs.setdefault('cwd', self.project_path)
278 kwargs.setdefault('stdout', self.VOID) 286 kwargs.setdefault('stdout', self.VOID)
287 kwargs.setdefault('timeout', GLOBAL_TIMEOUT)
279 return subprocess2.check_call_out( 288 return subprocess2.check_call_out(
280 self._add_svn_flags(args, False), **kwargs) 289 self._add_svn_flags(args, False),
290 **kwargs)
281 291
282 def _check_output_svn(self, args, credentials=True, **kwargs): 292 def _check_output_svn(self, args, credentials=True, **kwargs):
283 """Runs svn and throws an exception if the command failed. 293 """Runs svn and throws an exception if the command failed.
284 294
285 Returns the output. 295 Returns the output.
286 """ 296 """
287 kwargs.setdefault('cwd', self.project_path) 297 kwargs.setdefault('cwd', self.project_path)
288 return subprocess2.check_output( 298 return subprocess2.check_output(
289 self._add_svn_flags(args, True, credentials), 299 self._add_svn_flags(args, True, credentials),
290 stderr=subprocess2.STDOUT, 300 stderr=subprocess2.STDOUT,
301 timeout=GLOBAL_TIMEOUT,
291 **kwargs) 302 **kwargs)
292 303
293 @staticmethod 304 @staticmethod
294 def _parse_svn_info(output, key): 305 def _parse_svn_info(output, key):
295 """Returns value for key from svn info output. 306 """Returns value for key from svn info output.
296 307
297 Case insensitive. 308 Case insensitive.
298 """ 309 """
299 values = {} 310 values = {}
300 key = key.lower() 311 key = key.lower()
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after
380 if p.diff_hunks: 391 if p.diff_hunks:
381 cmd = [ 392 cmd = [
382 'patch', 393 'patch',
383 '-p%s' % p.patchlevel, 394 '-p%s' % p.patchlevel,
384 '--forward', 395 '--forward',
385 '--force', 396 '--force',
386 '--no-backup-if-mismatch', 397 '--no-backup-if-mismatch',
387 ] 398 ]
388 stdout.append( 399 stdout.append(
389 subprocess2.check_output( 400 subprocess2.check_output(
390 cmd, stdin=p.get(False), cwd=self.project_path)) 401 cmd,
402 stdin=p.get(False),
403 cwd=self.project_path,
404 timeout=GLOBAL_TIMEOUT))
391 elif p.is_new and not os.path.exists(filepath): 405 elif p.is_new and not os.path.exists(filepath):
392 # There is only a header. Just create the file if it doesn't 406 # There is only a header. Just create the file if it doesn't
393 # exist. 407 # exist.
394 open(filepath, 'w').close() 408 open(filepath, 'w').close()
395 stdout.append('Created an empty file.') 409 stdout.append('Created an empty file.')
396 if p.is_new and not p.source_filename: 410 if p.is_new and not p.source_filename:
397 # Do not run it if p.source_filename is defined, since svn copy was 411 # Do not run it if p.source_filename is defined, since svn copy was
398 # using above. 412 # using above.
399 stdout.append( 413 stdout.append(
400 self._check_output_svn( 414 self._check_output_svn(
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after
476 """Reverts local modifications or checks out if the directory is not 490 """Reverts local modifications or checks out if the directory is not
477 present. Use depot_tools's functionality to do this. 491 present. Use depot_tools's functionality to do this.
478 """ 492 """
479 flags = ['--ignore-externals'] 493 flags = ['--ignore-externals']
480 if revision: 494 if revision:
481 flags.extend(['--revision', str(revision)]) 495 flags.extend(['--revision', str(revision)])
482 if not os.path.isdir(self.project_path): 496 if not os.path.isdir(self.project_path):
483 logging.info( 497 logging.info(
484 'Directory %s is not present, checking it out.' % self.project_path) 498 'Directory %s is not present, checking it out.' % self.project_path)
485 self._check_call_svn( 499 self._check_call_svn(
486 ['checkout', self.svn_url, self.project_path] + flags, cwd=None) 500 ['checkout', self.svn_url, self.project_path] + flags,
501 cwd=None,
502 timeout=FETCH_TIMEOUT)
Peter Mayo 2012/11/08 19:04:59 I would think checkout and update may be different
M-A Ruel 2012/11/08 19:07:57 I think it's fine. I'll change otherwise but I'm c
487 else: 503 else:
504 # TODO(maruel): This command will shell out without a timeout.
488 scm.SVN.Revert(self.project_path, no_ignore=True) 505 scm.SVN.Revert(self.project_path, no_ignore=True)
489 # Revive files that were deleted in scm.SVN.Revert(). 506 # Revive files that were deleted in scm.SVN.Revert().
490 self._check_call_svn(['update', '--force'] + flags) 507 self._check_call_svn(['update', '--force'] + flags, timeout=FETCH_TIMEOUT)
491 return self._get_revision() 508 return self._get_revision()
492 509
493 def _get_revision(self): 510 def _get_revision(self):
494 out = self._check_output_svn(['info', '.']) 511 out = self._check_output_svn(['info', '.'])
495 revision = int(self._parse_svn_info(out, 'revision')) 512 revision = int(self._parse_svn_info(out, 'revision'))
496 if revision != self._last_seen_revision: 513 if revision != self._last_seen_revision:
497 logging.info('Updated to revision %d' % revision) 514 logging.info('Updated to revision %d' % revision)
498 self._last_seen_revision = revision 515 self._last_seen_revision = revision
499 return revision 516 return revision
500 517
(...skipping 29 matching lines...) Expand all
530 547
531 Checks it out if not present and deletes the working branch. 548 Checks it out if not present and deletes the working branch.
532 """ 549 """
533 assert self.remote_branch 550 assert self.remote_branch
534 assert os.path.isdir(self.project_path) 551 assert os.path.isdir(self.project_path)
535 self._check_call_git(['reset', '--hard', '--quiet']) 552 self._check_call_git(['reset', '--hard', '--quiet'])
536 if revision: 553 if revision:
537 try: 554 try:
538 revision = self._check_output_git(['rev-parse', revision]) 555 revision = self._check_output_git(['rev-parse', revision])
539 except subprocess.CalledProcessError: 556 except subprocess.CalledProcessError:
540 self._check_call_git( 557 self._fetch_remote()
541 ['fetch', self.remote, self.remote_branch, '--quiet'])
542 revision = self._check_output_git(['rev-parse', revision]) 558 revision = self._check_output_git(['rev-parse', revision])
543 self._check_call_git(['checkout', '--force', '--quiet', revision]) 559 self._check_call_git(['checkout', '--force', '--quiet', revision])
544 else: 560 else:
545 branches, active = self._branches() 561 branches, active = self._branches()
546 if active != 'master': 562 if active != 'master':
547 self._check_call_git(['checkout', '--force', '--quiet', 'master']) 563 self._check_call_git(['checkout', '--force', '--quiet', 'master'])
548 self._check_call_git(['pull', self.remote, self.remote_branch, '--quiet']) 564 self._check_call_git(['pull', self.remote, self.remote_branch, '--quiet'])
549 if self.working_branch in branches: 565 if self.working_branch in branches:
550 self._call_git(['branch', '-D', self.working_branch]) 566 self._call_git(['branch', '-D', self.working_branch])
551 567
(...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after
642 658
643 Subclass needs to dcommit or push. 659 Subclass needs to dcommit or push.
644 """ 660 """
645 assert isinstance(commit_message, unicode) 661 assert isinstance(commit_message, unicode)
646 self._check_call_git(['commit', '--amend', '-m', commit_message]) 662 self._check_call_git(['commit', '--amend', '-m', commit_message])
647 return self._check_output_git(['rev-parse', 'HEAD']).strip() 663 return self._check_output_git(['rev-parse', 'HEAD']).strip()
648 664
649 def _check_call_git(self, args, **kwargs): 665 def _check_call_git(self, args, **kwargs):
650 kwargs.setdefault('cwd', self.project_path) 666 kwargs.setdefault('cwd', self.project_path)
651 kwargs.setdefault('stdout', self.VOID) 667 kwargs.setdefault('stdout', self.VOID)
652 return subprocess2.check_call_out(['git'] + args, **kwargs) 668 kwargs.setdefault('timeout', GLOBAL_TIMEOUT)
669 return subprocess2.check_call_out(
670 ['git'] + args, **kwargs)
653 671
654 def _call_git(self, args, **kwargs): 672 def _call_git(self, args, **kwargs):
655 """Like check_call but doesn't throw on failure.""" 673 """Like check_call but doesn't throw on failure."""
656 kwargs.setdefault('cwd', self.project_path) 674 kwargs.setdefault('cwd', self.project_path)
657 kwargs.setdefault('stdout', self.VOID) 675 kwargs.setdefault('stdout', self.VOID)
658 return subprocess2.call(['git'] + args, **kwargs) 676 return subprocess2.call(['git'] + args, timeout=GLOBAL_TIMEOUT, **kwargs)
659 677
660 def _check_output_git(self, args, **kwargs): 678 def _check_output_git(self, args, **kwargs):
661 kwargs.setdefault('cwd', self.project_path) 679 kwargs.setdefault('cwd', self.project_path)
662 return subprocess2.check_output( 680 return subprocess2.check_output(
663 ['git'] + args, stderr=subprocess2.STDOUT, **kwargs) 681 ['git'] + args,
682 stderr=subprocess2.STDOUT,
683 timeout=GLOBAL_TIMEOUT,
684 **kwargs)
664 685
665 def _branches(self): 686 def _branches(self):
666 """Returns the list of branches and the active one.""" 687 """Returns the list of branches and the active one."""
667 out = self._check_output_git(['branch']).splitlines(False) 688 out = self._check_output_git(['branch']).splitlines(False)
668 branches = [l[2:] for l in out] 689 branches = [l[2:] for l in out]
669 active = None 690 active = None
670 for l in out: 691 for l in out:
671 if l.startswith('*'): 692 if l.startswith('*'):
672 active = l[2:] 693 active = l[2:]
673 break 694 break
(...skipping 14 matching lines...) Expand all
688 709
689 def _fetch_remote(self): 710 def _fetch_remote(self):
690 """Fetches the remote without rebasing.""" 711 """Fetches the remote without rebasing."""
691 raise NotImplementedError() 712 raise NotImplementedError()
692 713
693 714
694 class GitCheckout(GitCheckoutBase): 715 class GitCheckout(GitCheckoutBase):
695 """Git checkout implementation.""" 716 """Git checkout implementation."""
696 def _fetch_remote(self): 717 def _fetch_remote(self):
697 # git fetch is always verbose even with -q -q so redirect its output. 718 # git fetch is always verbose even with -q -q so redirect its output.
698 self._check_output_git(['fetch', self.remote, self.remote_branch]) 719 self._check_call_git(
720 ['fetch', self.remote, self.remote_branch, '--quiet'],
Peter Mayo 2012/11/08 19:04:59 Add quiet to CL description.
721 timeout=FETCH_TIMEOUT)
699 722
700 723
701 class ReadOnlyCheckout(object): 724 class ReadOnlyCheckout(object):
702 """Converts a checkout into a read-only one.""" 725 """Converts a checkout into a read-only one."""
703 def __init__(self, checkout, post_processors=None): 726 def __init__(self, checkout, post_processors=None):
704 super(ReadOnlyCheckout, self).__init__() 727 super(ReadOnlyCheckout, self).__init__()
705 self.checkout = checkout 728 self.checkout = checkout
706 self.post_processors = (post_processors or []) + ( 729 self.post_processors = (post_processors or []) + (
707 self.checkout.post_processors or []) 730 self.checkout.post_processors or [])
708 731
(...skipping 15 matching lines...) Expand all
724 def revisions(self, rev1, rev2): 747 def revisions(self, rev1, rev2):
725 return self.checkout.revisions(rev1, rev2) 748 return self.checkout.revisions(rev1, rev2)
726 749
727 @property 750 @property
728 def project_name(self): 751 def project_name(self):
729 return self.checkout.project_name 752 return self.checkout.project_name
730 753
731 @property 754 @property
732 def project_path(self): 755 def project_path(self):
733 return self.checkout.project_path 756 return self.checkout.project_path
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698