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

Unified Diff: tools/push-to-trunk/common_includes.py

Issue 110573004: Merge bleeding_edge 17696:18016. (Closed) Base URL: https://v8.googlecode.com/svn/branches/experimental/parser
Patch Set: Created 7 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 side-by-side diff with in-line comments
Download patch
Index: tools/push-to-trunk/common_includes.py
diff --git a/tools/push-to-trunk/common_includes.py b/tools/push-to-trunk/common_includes.py
index eb2bfb07b3549838395dbc363b62d52db9383656..eaa3d201728a85c4e42e1ae170de76d5f76467d8 100644
--- a/tools/push-to-trunk/common_includes.py
+++ b/tools/push-to-trunk/common_includes.py
@@ -30,6 +30,8 @@ import os
import re
import subprocess
import sys
+import textwrap
+import urllib2
PERSISTFILE_BASENAME = "PERSISTFILE_BASENAME"
TEMP_BRANCH = "TEMP_BRANCH"
@@ -67,6 +69,15 @@ def MSub(rexp, replacement, text):
return re.sub(rexp, replacement, text, flags=re.MULTILINE)
+def Fill80(line):
+ # Replace tabs and remove surrounding space.
+ line = re.sub(r"\t", r" ", line.strip())
+
+ # Format with 8 characters indentation and line width 80.
+ return textwrap.fill(line, width=80, initial_indent=" ",
+ subsequent_indent=" ")
+
+
def GetLastChangeLogEntries(change_log_file):
result = []
for line in LinesInFile(change_log_file):
@@ -75,6 +86,95 @@ def GetLastChangeLogEntries(change_log_file):
return "".join(result)
+def MakeComment(text):
+ return MSub(r"^( ?)", "#", text)
+
+
+def StripComments(text):
+ # Use split not splitlines to keep terminal newlines.
+ return "\n".join(filter(lambda x: not x.startswith("#"), text.split("\n")))
+
+
+def MakeChangeLogBody(commit_messages, auto_format=False):
+ result = ""
+ added_titles = set()
+ for (title, body, author) in commit_messages:
+ # TODO(machenbach): Reload the commit description from rietveld in order to
+ # catch late changes.
+ title = title.strip()
+ if auto_format:
+ # Only add commits that set the LOG flag correctly.
+ log_exp = r"^[ \t]*LOG[ \t]*=[ \t]*(?:Y(?:ES)?)|TRUE"
+ if not re.search(log_exp, body, flags=re.I | re.M):
+ continue
+ # Never include reverts.
+ if title.startswith("Revert "):
+ continue
+ # Don't include duplicates.
+ if title in added_titles:
+ continue
+
+ # TODO(machenbach): Let python do all formatting. Get raw git title, attach
+ # issue and add/move dot to the end - all in one line. Make formatting and
+ # indentation afterwards.
+
+ # Add the commit's title line.
+ result += "%s\n" % Fill80(title)
+ added_titles.add(title)
+
+ # Add bug references.
+ result += MakeChangeLogBugReference(body)
+
+ # Append the commit's author for reference if not in auto-format mode.
+ if not auto_format:
+ result += "%s\n" % Fill80("(%s)" % author.strip())
+
+ result += "\n"
+ return result
+
+
+def MakeChangeLogBugReference(body):
+ """Grep for "BUG=xxxx" lines in the commit message and convert them to
+ "(issue xxxx)".
+ """
+ crbugs = []
+ v8bugs = []
+
+ def AddIssues(text):
+ ref = re.match(r"^BUG[ \t]*=[ \t]*(.+)$", text.strip())
+ if not ref:
+ return
+ for bug in ref.group(1).split(","):
+ bug = bug.strip()
+ match = re.match(r"^v8:(\d+)$", bug)
+ if match: v8bugs.append(int(match.group(1)))
+ else:
+ match = re.match(r"^(?:chromium:)?(\d+)$", bug)
+ if match: crbugs.append(int(match.group(1)))
+
+ # Add issues to crbugs and v8bugs.
+ map(AddIssues, body.splitlines())
+
+ # Filter duplicates, sort, stringify.
+ crbugs = map(str, sorted(set(crbugs)))
+ v8bugs = map(str, sorted(set(v8bugs)))
+
+ bug_groups = []
+ def FormatIssues(prefix, bugs):
+ if len(bugs) > 0:
+ plural = "s" if len(bugs) > 1 else ""
+ bug_groups.append("%sissue%s %s" % (prefix, plural, ", ".join(bugs)))
+
+ FormatIssues("", v8bugs)
+ FormatIssues("Chromium ", crbugs)
+
+ if len(bug_groups) > 0:
+ # Format with 8 characters indentation and max 80 character lines.
+ return "%s\n" % Fill80("(%s)" % ", ".join(bug_groups))
+ else:
+ return ""
+
+
# Some commands don't like the pipe, e.g. calling vi from within the script or
# from subscripts like git cl upload.
def Command(cmd, args="", prefix="", pipe=True):
@@ -97,39 +197,35 @@ class SideEffectHandler(object):
def ReadLine(self):
return sys.stdin.readline().strip()
+ def ReadURL(self, url):
+ # pylint: disable=E1121
+ url_fh = urllib2.urlopen(url, None, 60)
+ try:
+ return url_fh.read()
+ finally:
+ url_fh.close()
+
DEFAULT_SIDE_EFFECT_HANDLER = SideEffectHandler()
class Step(object):
- def __init__(self, text="", requires=None):
+ def __init__(self, text, requires, number, config, state, options, handler):
self._text = text
- self._number = -1
self._requires = requires
- self._side_effect_handler = DEFAULT_SIDE_EFFECT_HANDLER
-
- def SetNumber(self, number):
self._number = number
-
- def SetConfig(self, config):
self._config = config
-
- def SetState(self, state):
self._state = state
-
- def SetOptions(self, options):
self._options = options
-
- def SetSideEffectHandler(self, handler):
self._side_effect_handler = handler
+ assert self._number >= 0
+ assert self._config is not None
+ assert self._state is not None
+ assert self._side_effect_handler is not None
def Config(self, key):
return self._config[key]
def Run(self):
- assert self._number >= 0
- assert self._config is not None
- assert self._state is not None
- assert self._side_effect_handler is not None
if self._requires:
self.RestoreIfUnset(self._requires)
if not self._state[self._requires]:
@@ -140,8 +236,13 @@ class Step(object):
def RunStep(self):
raise NotImplementedError
- def ReadLine(self):
- return self._side_effect_handler.ReadLine()
+ def ReadLine(self, default=None):
+ # Don't prompt in forced mode.
+ if self._options and self._options.f and default is not None:
+ print "%s (forced)" % default
+ return default
+ else:
+ return self._side_effect_handler.ReadLine()
def Git(self, args="", prefix="", pipe=True):
return self._side_effect_handler.Command("git", args, prefix, pipe)
@@ -150,15 +251,23 @@ class Step(object):
return self._side_effect_handler.Command(os.environ["EDITOR"], args,
pipe=False)
+ def ReadURL(self, url):
+ return self._side_effect_handler.ReadURL(url)
+
def Die(self, msg=""):
if msg != "":
print "Error: %s" % msg
print "Exiting"
raise Exception(msg)
+ def DieInForcedMode(self, msg=""):
+ if self._options and self._options.f:
+ msg = msg or "Not implemented in forced mode."
+ self.Die(msg)
+
def Confirm(self, msg):
print "%s [Y/n] " % msg,
- answer = self.ReadLine()
+ answer = self.ReadLine(default="Y")
return answer == "" or answer == "Y" or answer == "y"
def DeleteBranch(self, name):
@@ -192,6 +301,8 @@ class Step(object):
if not os.path.exists(self._config[DOT_GIT_LOCATION]):
self.Die("This is not a git checkout, this script won't work for you.")
+ # TODO(machenbach): Don't use EDITOR in forced mode as soon as script is
+ # well tested.
# Cancel if EDITOR is unset or not executable.
if (not os.environ.get("EDITOR") or
Command("which", os.environ["EDITOR"]) is None):
@@ -216,8 +327,10 @@ class Step(object):
if self.Git("svn fetch") is None:
self.Die("'git svn fetch' failed.")
+ def PrepareBranch(self):
# Get ahold of a safe temporary branch and check it out.
- if current_branch != self._config[TEMP_BRANCH]:
+ self.RestoreIfUnset("current_branch")
+ if self._state["current_branch"] != self._config[TEMP_BRANCH]:
self.DeleteBranch(self._config[TEMP_BRANCH])
self.Git("checkout -b %s" % self._config[TEMP_BRANCH])
@@ -261,6 +374,8 @@ class Step(object):
answer = ""
while answer != "LGTM":
print "> ",
+ # TODO(machenbach): Add default="LGTM" to avoid prompt when script is
+ # well tested and when prepare push cl has TBR flag.
answer = self.ReadLine()
if answer != "LGTM":
print "That was not 'LGTM'."
@@ -269,6 +384,7 @@ class Step(object):
print("Applying the patch \"%s\" failed. Either type \"ABORT<Return>\", "
"or resolve the conflicts, stage *all* touched files with "
"'git add', and type \"RESOLVED<Return>\"")
+ self.DieInForcedMode()
answer = ""
while answer != "RESOLVED":
if answer == "ABORT":
@@ -286,12 +402,53 @@ class Step(object):
class UploadStep(Step):
- def __init__(self):
- Step.__init__(self, "Upload for code review.")
+ MESSAGE = "Upload for code review."
def RunStep(self):
- print "Please enter the email address of a V8 reviewer for your patch: ",
- reviewer = self.ReadLine()
- args = "cl upload -r \"%s\" --send-mail" % reviewer
- if self.Git(args,pipe=False) is None:
+ if self._options.r:
+ print "Using account %s for review." % self._options.r
+ reviewer = self._options.r
+ else:
+ print "Please enter the email address of a V8 reviewer for your patch: ",
+ self.DieInForcedMode("A reviewer must be specified in forced mode.")
+ reviewer = self.ReadLine()
+ force_flag = " -f" if self._options.f else ""
+ args = "cl upload -r \"%s\" --send-mail%s" % (reviewer, force_flag)
+ # TODO(machenbach): Check output in forced mode. Verify that all required
+ # base files were uploaded, if not retry.
+ if self.Git(args, pipe=False) is None:
self.Die("'git cl upload' failed, please try again.")
+
+
+def MakeStep(step_class=Step, number=0, state=None, config=None,
+ options=None, side_effect_handler=DEFAULT_SIDE_EFFECT_HANDLER):
+ # Allow to pass in empty dictionaries.
+ state = state if state is not None else {}
+ config = config if config is not None else {}
+
+ try:
+ message = step_class.MESSAGE
+ except AttributeError:
+ message = step_class.__name__
+ try:
+ requires = step_class.REQUIRES
+ except AttributeError:
+ requires = None
+
+ return step_class(message, requires, number=number, config=config,
+ state=state, options=options,
+ handler=side_effect_handler)
+
+
+def RunScript(step_classes,
+ config,
+ options,
+ side_effect_handler=DEFAULT_SIDE_EFFECT_HANDLER):
+ state = {}
+ steps = []
+ for (number, step_class) in enumerate(step_classes):
+ steps.append(MakeStep(step_class, number, state, config,
+ options, side_effect_handler))
+
+ for step in steps[options.s:]:
+ step.Run()

Powered by Google App Engine
This is Rietveld 408576698