| 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 06b7ebe53a3c3655e41048c186bef308b59402c0..545fab382fe6a05f59b506cc906a58c217138333 100644
|
| --- a/tools/push-to-trunk/common_includes.py
|
| +++ b/tools/push-to-trunk/common_includes.py
|
| @@ -30,6 +30,7 @@ import os
|
| import re
|
| import subprocess
|
| import sys
|
| +import textwrap
|
|
|
| PERSISTFILE_BASENAME = "PERSISTFILE_BASENAME"
|
| TEMP_BRANCH = "TEMP_BRANCH"
|
| @@ -67,6 +68,11 @@ def MSub(rexp, replacement, text):
|
| return re.sub(rexp, replacement, text, flags=re.MULTILINE)
|
|
|
|
|
| +def Fill80(line):
|
| + return textwrap.fill(line, width=80, initial_indent=" ",
|
| + subsequent_indent=" ")
|
| +
|
| +
|
| def GetLastChangeLogEntries(change_log_file):
|
| result = []
|
| for line in LinesInFile(change_log_file):
|
| @@ -81,28 +87,66 @@ def MakeChangeLogBody(commit_generator):
|
| # Add the commit's title line.
|
| result += "%s\n" % title.rstrip()
|
|
|
| - # Grep for "BUG=xxxx" lines in the commit message and convert them to
|
| - # "(issue xxxx)".
|
| - out = body.splitlines()
|
| - out = filter(lambda x: re.search(r"^BUG=", x), out)
|
| - out = filter(lambda x: not re.search(r"BUG=$", x), out)
|
| - out = filter(lambda x: not re.search(r"BUG=none$", x), out)
|
| -
|
| - # TODO(machenbach): Handle multiple entries (e.g. BUG=123, 234).
|
| - def FormatIssue(text):
|
| - text = re.sub(r"BUG=v8:(.*)$", r"(issue \1)", text)
|
| - text = re.sub(r"BUG=chromium:(.*)$", r"(Chromium issue \1)", text)
|
| - text = re.sub(r"BUG=(.*)$", r"(Chromium issue \1)", text)
|
| - return " %s\n" % text
|
| -
|
| - for line in map(FormatIssue, out):
|
| - result += line
|
| + # Add bug references.
|
| + result += MakeChangeLogBugReference(body)
|
|
|
| # Append the commit's author for reference.
|
| result += "%s\n\n" % author.rstrip()
|
| return result
|
|
|
|
|
| +def MakeChangeLogBugReference(body):
|
| + # Grep for "BUG=xxxx" lines in the commit message and convert them to
|
| + # "(issue xxxx)".
|
| + out = body.splitlines()
|
| + out = filter(lambda x: re.search(r"^[ \t]*BUG[ \t]*=", x), out)
|
| + out = filter(lambda x: not re.search(r"BUG[ \t]*=[ \t]*$", x), out)
|
| + out = filter(lambda x: not re.search(r"BUG[ \t]*=[ \t]*none[ \t]*$", x), out)
|
| +
|
| + crbugs = []
|
| + v8bugs = []
|
| +
|
| + def AddSafe(bugs, bug):
|
| + try:
|
| + bugs.append(int(bug))
|
| + except ValueError:
|
| + pass
|
| +
|
| + def AddIssues(text):
|
| + ref = re.match(r"^[ \t]*BUG[ \t]*=[ \t]*(.*?)[ \t]*$", text)
|
| + if not ref:
|
| + return
|
| + for bug in ref.group(1).split(","):
|
| + bug = bug.strip()
|
| + match = re.match(r"^v8[ \t]*:[ \t]*(.*)$", bug)
|
| + if match: AddSafe(v8bugs, match.group(1))
|
| + else:
|
| + match = re.match(r"^(?:chromium[ \t]*:)?[ \t]*(.*)$", bug)
|
| + if match: AddSafe(crbugs, match.group(1))
|
| +
|
| + # Add issues to crbugs and v8bugs.
|
| + map(AddIssues, out)
|
| +
|
| + # 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("Chromium ", crbugs)
|
| + FormatIssues("", v8bugs)
|
| +
|
| + 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):
|
| @@ -132,6 +176,7 @@ class Step(object):
|
| def __init__(self, text="", requires=None):
|
| self._text = text
|
| self._number = -1
|
| + self._options = None
|
| self._requires = requires
|
| self._side_effect_handler = DEFAULT_SIDE_EFFECT_HANDLER
|
|
|
| @@ -168,8 +213,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)
|
| @@ -184,9 +234,14 @@ class Step(object):
|
| 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):
|
| @@ -220,6 +275,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):
|
| @@ -291,6 +348,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'."
|
| @@ -299,6 +358,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":
|
| @@ -320,8 +380,13 @@ class UploadStep(Step):
|
| Step.__init__(self, "Upload for code review.")
|
|
|
| def RunStep(self):
|
| - print "Please enter the email address of a V8 reviewer for your patch: ",
|
| - reviewer = self.ReadLine()
|
| + if self._options and 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()
|
| args = "cl upload -r \"%s\" --send-mail" % reviewer
|
| if self.Git(args,pipe=False) is None:
|
| self.Die("'git cl upload' failed, please try again.")
|
|
|