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.") |