| OLD | NEW |
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright 2013 the V8 project authors. All rights reserved. | 2 # Copyright 2013 the V8 project authors. All rights reserved. |
| 3 # Redistribution and use in source and binary forms, with or without | 3 # Redistribution and use in source and binary forms, with or without |
| 4 # modification, are permitted provided that the following conditions are | 4 # modification, are permitted provided that the following conditions are |
| 5 # met: | 5 # met: |
| 6 # | 6 # |
| 7 # * Redistributions of source code must retain the above copyright | 7 # * Redistributions of source code must retain the above copyright |
| 8 # notice, this list of conditions and the following disclaimer. | 8 # notice, this list of conditions and the following disclaimer. |
| 9 # * Redistributions in binary form must reproduce the above | 9 # * Redistributions in binary form must reproduce the above |
| 10 # copyright notice, this list of conditions and the following | 10 # copyright notice, this list of conditions and the following |
| (...skipping 11 matching lines...) Expand all Loading... |
| 22 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 22 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 23 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 23 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 24 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 24 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 28 | 28 |
| 29 import argparse | 29 import argparse |
| 30 import datetime | 30 import datetime |
| 31 import httplib | 31 import httplib |
| 32 import glob |
| 32 import imp | 33 import imp |
| 33 import json | 34 import json |
| 34 import os | 35 import os |
| 35 import re | 36 import re |
| 37 import shutil |
| 36 import subprocess | 38 import subprocess |
| 37 import sys | 39 import sys |
| 38 import textwrap | 40 import textwrap |
| 39 import time | 41 import time |
| 40 import urllib | 42 import urllib |
| 41 import urllib2 | 43 import urllib2 |
| 42 | 44 |
| 43 from git_recipes import GitRecipesMixin | 45 from git_recipes import GitRecipesMixin |
| 44 from git_recipes import GitFailedException | 46 from git_recipes import GitFailedException |
| 45 | 47 |
| 46 PERSISTFILE_BASENAME = "PERSISTFILE_BASENAME" | 48 PERSISTFILE_BASENAME = "PERSISTFILE_BASENAME" |
| 47 BRANCHNAME = "BRANCHNAME" | 49 BRANCHNAME = "BRANCHNAME" |
| 48 DOT_GIT_LOCATION = "DOT_GIT_LOCATION" | 50 DOT_GIT_LOCATION = "DOT_GIT_LOCATION" |
| 49 VERSION_FILE = "VERSION_FILE" | 51 VERSION_FILE = "VERSION_FILE" |
| 50 CHANGELOG_FILE = "CHANGELOG_FILE" | 52 CHANGELOG_FILE = "CHANGELOG_FILE" |
| 51 CHANGELOG_ENTRY_FILE = "CHANGELOG_ENTRY_FILE" | 53 CHANGELOG_ENTRY_FILE = "CHANGELOG_ENTRY_FILE" |
| 52 COMMITMSG_FILE = "COMMITMSG_FILE" | 54 COMMITMSG_FILE = "COMMITMSG_FILE" |
| 53 PATCH_FILE = "PATCH_FILE" | 55 PATCH_FILE = "PATCH_FILE" |
| 54 | 56 |
| 57 # V8 base directory. |
| 58 DEFAULT_CWD = os.path.dirname( |
| 59 os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) |
| 60 |
| 55 | 61 |
| 56 def TextToFile(text, file_name): | 62 def TextToFile(text, file_name): |
| 57 with open(file_name, "w") as f: | 63 with open(file_name, "w") as f: |
| 58 f.write(text) | 64 f.write(text) |
| 59 | 65 |
| 60 | 66 |
| 61 def AppendToFile(text, file_name): | 67 def AppendToFile(text, file_name): |
| 62 with open(file_name, "a") as f: | 68 with open(file_name, "a") as f: |
| 63 f.write(text) | 69 f.write(text) |
| 64 | 70 |
| (...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 176 version_keys = map(int, version.split(".")) | 182 version_keys = map(int, version.split(".")) |
| 177 # Fill up to full version numbers to normalize comparison. | 183 # Fill up to full version numbers to normalize comparison. |
| 178 while len(version_keys) < 4: # pragma: no cover | 184 while len(version_keys) < 4: # pragma: no cover |
| 179 version_keys.append(0) | 185 version_keys.append(0) |
| 180 # Fill digits. | 186 # Fill digits. |
| 181 return ".".join(map("{0:04d}".format, version_keys)) | 187 return ".".join(map("{0:04d}".format, version_keys)) |
| 182 | 188 |
| 183 | 189 |
| 184 # Some commands don't like the pipe, e.g. calling vi from within the script or | 190 # Some commands don't like the pipe, e.g. calling vi from within the script or |
| 185 # from subscripts like git cl upload. | 191 # from subscripts like git cl upload. |
| 186 def Command(cmd, args="", prefix="", pipe=True): | 192 def Command(cmd, args="", prefix="", pipe=True, cwd=None): |
| 193 cwd = cwd or os.getcwd() |
| 187 # TODO(machenbach): Use timeout. | 194 # TODO(machenbach): Use timeout. |
| 188 cmd_line = "%s %s %s" % (prefix, cmd, args) | 195 cmd_line = "%s %s %s" % (prefix, cmd, args) |
| 189 print "Command: %s" % cmd_line | 196 print "Command: %s" % cmd_line |
| 197 print "in %s" % cwd |
| 190 sys.stdout.flush() | 198 sys.stdout.flush() |
| 191 try: | 199 try: |
| 192 if pipe: | 200 if pipe: |
| 193 return subprocess.check_output(cmd_line, shell=True) | 201 return subprocess.check_output(cmd_line, shell=True, cwd=cwd) |
| 194 else: | 202 else: |
| 195 return subprocess.check_call(cmd_line, shell=True) | 203 return subprocess.check_call(cmd_line, shell=True, cwd=cwd) |
| 196 except subprocess.CalledProcessError: | 204 except subprocess.CalledProcessError: |
| 197 return None | 205 return None |
| 198 finally: | 206 finally: |
| 199 sys.stdout.flush() | 207 sys.stdout.flush() |
| 200 sys.stderr.flush() | 208 sys.stderr.flush() |
| 201 | 209 |
| 202 | 210 |
| 203 # Wrapper for side effects. | 211 # Wrapper for side effects. |
| 204 class SideEffectHandler(object): # pragma: no cover | 212 class SideEffectHandler(object): # pragma: no cover |
| 205 def Call(self, fun, *args, **kwargs): | 213 def Call(self, fun, *args, **kwargs): |
| 206 return fun(*args, **kwargs) | 214 return fun(*args, **kwargs) |
| 207 | 215 |
| 208 def Command(self, cmd, args="", prefix="", pipe=True): | 216 def Command(self, cmd, args="", prefix="", pipe=True, cwd=None): |
| 209 return Command(cmd, args, prefix, pipe) | 217 return Command(cmd, args, prefix, pipe, cwd=cwd) |
| 210 | 218 |
| 211 def ReadLine(self): | 219 def ReadLine(self): |
| 212 return sys.stdin.readline().strip() | 220 return sys.stdin.readline().strip() |
| 213 | 221 |
| 214 def ReadURL(self, url, params=None): | 222 def ReadURL(self, url, params=None): |
| 215 # pylint: disable=E1121 | 223 # pylint: disable=E1121 |
| 216 url_fh = urllib2.urlopen(url, params, 60) | 224 url_fh = urllib2.urlopen(url, params, 60) |
| 217 try: | 225 try: |
| 218 return url_fh.read() | 226 return url_fh.read() |
| 219 finally: | 227 finally: |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 256 | 264 |
| 257 class Step(GitRecipesMixin): | 265 class Step(GitRecipesMixin): |
| 258 def __init__(self, text, requires, number, config, state, options, handler): | 266 def __init__(self, text, requires, number, config, state, options, handler): |
| 259 self._text = text | 267 self._text = text |
| 260 self._requires = requires | 268 self._requires = requires |
| 261 self._number = number | 269 self._number = number |
| 262 self._config = config | 270 self._config = config |
| 263 self._state = state | 271 self._state = state |
| 264 self._options = options | 272 self._options = options |
| 265 self._side_effect_handler = handler | 273 self._side_effect_handler = handler |
| 274 |
| 275 # The testing configuration might set a different default cwd. |
| 276 self.default_cwd = self._config.get("DEFAULT_CWD") or DEFAULT_CWD |
| 277 |
| 266 assert self._number >= 0 | 278 assert self._number >= 0 |
| 267 assert self._config is not None | 279 assert self._config is not None |
| 268 assert self._state is not None | 280 assert self._state is not None |
| 269 assert self._side_effect_handler is not None | 281 assert self._side_effect_handler is not None |
| 270 | 282 |
| 271 def __getitem__(self, key): | 283 def __getitem__(self, key): |
| 272 # Convenience method to allow direct [] access on step classes for | 284 # Convenience method to allow direct [] access on step classes for |
| 273 # manipulating the backed state dict. | 285 # manipulating the backed state dict. |
| 274 return self._state[key] | 286 return self._state[key] |
| 275 | 287 |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 334 return result | 346 return result |
| 335 | 347 |
| 336 def ReadLine(self, default=None): | 348 def ReadLine(self, default=None): |
| 337 # Don't prompt in forced mode. | 349 # Don't prompt in forced mode. |
| 338 if self._options.force_readline_defaults and default is not None: | 350 if self._options.force_readline_defaults and default is not None: |
| 339 print "%s (forced)" % default | 351 print "%s (forced)" % default |
| 340 return default | 352 return default |
| 341 else: | 353 else: |
| 342 return self._side_effect_handler.ReadLine() | 354 return self._side_effect_handler.ReadLine() |
| 343 | 355 |
| 344 def Git(self, args="", prefix="", pipe=True, retry_on=None): | 356 def Command(self, name, args, cwd=None): |
| 345 cmd = lambda: self._side_effect_handler.Command("git", args, prefix, pipe) | 357 cmd = lambda: self._side_effect_handler.Command( |
| 358 name, args, "", True, cwd=cwd or self.default_cwd) |
| 359 return self.Retry(cmd, None, [5]) |
| 360 |
| 361 def Git(self, args="", prefix="", pipe=True, retry_on=None, cwd=None): |
| 362 cmd = lambda: self._side_effect_handler.Command( |
| 363 "git", args, prefix, pipe, cwd=cwd or self.default_cwd) |
| 346 result = self.Retry(cmd, retry_on, [5, 30]) | 364 result = self.Retry(cmd, retry_on, [5, 30]) |
| 347 if result is None: | 365 if result is None: |
| 348 raise GitFailedException("'git %s' failed." % args) | 366 raise GitFailedException("'git %s' failed." % args) |
| 349 return result | 367 return result |
| 350 | 368 |
| 351 def SVN(self, args="", prefix="", pipe=True, retry_on=None): | 369 def SVN(self, args="", prefix="", pipe=True, retry_on=None, cwd=None): |
| 352 cmd = lambda: self._side_effect_handler.Command("svn", args, prefix, pipe) | 370 cmd = lambda: self._side_effect_handler.Command( |
| 371 "svn", args, prefix, pipe, cwd=cwd or self.default_cwd) |
| 353 return self.Retry(cmd, retry_on, [5, 30]) | 372 return self.Retry(cmd, retry_on, [5, 30]) |
| 354 | 373 |
| 355 def Editor(self, args): | 374 def Editor(self, args): |
| 356 if self._options.requires_editor: | 375 if self._options.requires_editor: |
| 357 return self._side_effect_handler.Command(os.environ["EDITOR"], args, | 376 return self._side_effect_handler.Command( |
| 358 pipe=False) | 377 os.environ["EDITOR"], |
| 378 args, |
| 379 pipe=False, |
| 380 cwd=self.default_cwd) |
| 359 | 381 |
| 360 def ReadURL(self, url, params=None, retry_on=None, wait_plan=None): | 382 def ReadURL(self, url, params=None, retry_on=None, wait_plan=None): |
| 361 wait_plan = wait_plan or [3, 60, 600] | 383 wait_plan = wait_plan or [3, 60, 600] |
| 362 cmd = lambda: self._side_effect_handler.ReadURL(url, params) | 384 cmd = lambda: self._side_effect_handler.ReadURL(url, params) |
| 363 return self.Retry(cmd, retry_on, wait_plan) | 385 return self.Retry(cmd, retry_on, wait_plan) |
| 364 | 386 |
| 365 def GetDate(self): | 387 def GetDate(self): |
| 366 return self._side_effect_handler.GetDate() | 388 return self._side_effect_handler.GetDate() |
| 367 | 389 |
| 368 def Die(self, msg=""): | 390 def Die(self, msg=""): |
| (...skipping 23 matching lines...) Expand all Loading... |
| 392 msg = "Can't continue. Please delete branch %s and try again." % name | 414 msg = "Can't continue. Please delete branch %s and try again." % name |
| 393 self.Die(msg) | 415 self.Die(msg) |
| 394 | 416 |
| 395 def InitialEnvironmentChecks(self): | 417 def InitialEnvironmentChecks(self): |
| 396 # Cancel if this is not a git checkout. | 418 # Cancel if this is not a git checkout. |
| 397 if not os.path.exists(self._config[DOT_GIT_LOCATION]): # pragma: no cover | 419 if not os.path.exists(self._config[DOT_GIT_LOCATION]): # pragma: no cover |
| 398 self.Die("This is not a git checkout, this script won't work for you.") | 420 self.Die("This is not a git checkout, this script won't work for you.") |
| 399 | 421 |
| 400 # Cancel if EDITOR is unset or not executable. | 422 # Cancel if EDITOR is unset or not executable. |
| 401 if (self._options.requires_editor and (not os.environ.get("EDITOR") or | 423 if (self._options.requires_editor and (not os.environ.get("EDITOR") or |
| 402 Command("which", os.environ["EDITOR"]) is None)): # pragma: no cover | 424 self.Command( |
| 425 "which", os.environ["EDITOR"]) is None)): # pragma: no cover |
| 403 self.Die("Please set your EDITOR environment variable, you'll need it.") | 426 self.Die("Please set your EDITOR environment variable, you'll need it.") |
| 404 | 427 |
| 405 def CommonPrepare(self): | 428 def CommonPrepare(self): |
| 406 # Check for a clean workdir. | 429 # Check for a clean workdir. |
| 407 if not self.GitIsWorkdirClean(): # pragma: no cover | 430 if not self.GitIsWorkdirClean(): # pragma: no cover |
| 408 self.Die("Workspace is not clean. Please commit or undo your changes.") | 431 self.Die("Workspace is not clean. Please commit or undo your changes.") |
| 409 | 432 |
| 410 # Persist current branch. | 433 # Persist current branch. |
| 411 self["current_branch"] = self.GitCurrentBranch() | 434 self["current_branch"] = self.GitCurrentBranch() |
| 412 | 435 |
| 413 # Fetch unfetched revisions. | 436 # Fetch unfetched revisions. |
| 414 self.GitSVNFetch() | 437 self.GitSVNFetch() |
| 415 | 438 |
| 416 def PrepareBranch(self): | 439 def PrepareBranch(self): |
| 417 # Delete the branch that will be created later if it exists already. | 440 # Delete the branch that will be created later if it exists already. |
| 418 self.DeleteBranch(self._config[BRANCHNAME]) | 441 self.DeleteBranch(self._config[BRANCHNAME]) |
| 419 | 442 |
| 420 def CommonCleanup(self): | 443 def CommonCleanup(self): |
| 421 self.GitCheckout(self["current_branch"]) | 444 self.GitCheckout(self["current_branch"]) |
| 422 if self._config[BRANCHNAME] != self["current_branch"]: | 445 if self._config[BRANCHNAME] != self["current_branch"]: |
| 423 self.GitDeleteBranch(self._config[BRANCHNAME]) | 446 self.GitDeleteBranch(self._config[BRANCHNAME]) |
| 424 | 447 |
| 425 # Clean up all temporary files. | 448 # Clean up all temporary files. |
| 426 Command("rm", "-f %s*" % self._config[PERSISTFILE_BASENAME]) | 449 for f in glob.iglob("%s*" % self._config[PERSISTFILE_BASENAME]): |
| 450 if os.path.isfile(f): |
| 451 os.remove(f) |
| 452 if os.path.isdir(f): |
| 453 shutil.rmtree(f) |
| 427 | 454 |
| 428 def ReadAndPersistVersion(self, prefix=""): | 455 def ReadAndPersistVersion(self, prefix=""): |
| 429 def ReadAndPersist(var_name, def_name): | 456 def ReadAndPersist(var_name, def_name): |
| 430 match = re.match(r"^#define %s\s+(\d*)" % def_name, line) | 457 match = re.match(r"^#define %s\s+(\d*)" % def_name, line) |
| 431 if match: | 458 if match: |
| 432 value = match.group(1) | 459 value = match.group(1) |
| 433 self["%s%s" % (prefix, var_name)] = value | 460 self["%s%s" % (prefix, var_name)] = value |
| 434 for line in LinesInFile(self._config[VERSION_FILE]): | 461 for line in LinesInFile(self._config[VERSION_FILE]): |
| 435 for (var_name, def_name) in [("major", "MAJOR_VERSION"), | 462 for (var_name, def_name) in [("major", "MAJOR_VERSION"), |
| 436 ("minor", "MINOR_VERSION"), | 463 ("minor", "MINOR_VERSION"), |
| (...skipping 163 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 600 help="Path to the script mapping google accounts.") | 627 help="Path to the script mapping google accounts.") |
| 601 parser.add_argument("-r", "--reviewer", default="", | 628 parser.add_argument("-r", "--reviewer", default="", |
| 602 help="The account name to be used for reviews.") | 629 help="The account name to be used for reviews.") |
| 603 parser.add_argument("--sheriff", default=False, action="store_true", | 630 parser.add_argument("--sheriff", default=False, action="store_true", |
| 604 help=("Determine current sheriff to review CLs. On " | 631 help=("Determine current sheriff to review CLs. On " |
| 605 "success, this will overwrite the reviewer " | 632 "success, this will overwrite the reviewer " |
| 606 "option.")) | 633 "option.")) |
| 607 parser.add_argument("-s", "--step", | 634 parser.add_argument("-s", "--step", |
| 608 help="Specify the step where to start work. Default: 0.", | 635 help="Specify the step where to start work. Default: 0.", |
| 609 default=0, type=int) | 636 default=0, type=int) |
| 610 | |
| 611 self._PrepareOptions(parser) | 637 self._PrepareOptions(parser) |
| 612 | 638 |
| 613 if args is None: # pragma: no cover | 639 if args is None: # pragma: no cover |
| 614 options = parser.parse_args() | 640 options = parser.parse_args() |
| 615 else: | 641 else: |
| 616 options = parser.parse_args(args) | 642 options = parser.parse_args(args) |
| 617 | 643 |
| 618 # Process common options. | 644 # Process common options. |
| 619 if options.step < 0: # pragma: no cover | 645 if options.step < 0: # pragma: no cover |
| 620 print "Bad step number %d" % options.step | 646 print "Bad step number %d" % options.step |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 654 for (number, step_class) in enumerate(step_classes): | 680 for (number, step_class) in enumerate(step_classes): |
| 655 steps.append(MakeStep(step_class, number, self._state, self._config, | 681 steps.append(MakeStep(step_class, number, self._state, self._config, |
| 656 options, self._side_effect_handler)) | 682 options, self._side_effect_handler)) |
| 657 for step in steps[options.step:]: | 683 for step in steps[options.step:]: |
| 658 if step.Run(): | 684 if step.Run(): |
| 659 return 0 | 685 return 0 |
| 660 return 0 | 686 return 0 |
| 661 | 687 |
| 662 def Run(self, args=None): | 688 def Run(self, args=None): |
| 663 return self.RunSteps(self._Steps(), args) | 689 return self.RunSteps(self._Steps(), args) |
| OLD | NEW |