| 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 12 matching lines...) Expand all Loading... |
| 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 os | 29 import os |
| 30 import re | 30 import re |
| 31 import subprocess | 31 import subprocess |
| 32 import sys | 32 import sys |
| 33 import textwrap |
| 34 import urllib2 |
| 33 | 35 |
| 34 PERSISTFILE_BASENAME = "PERSISTFILE_BASENAME" | 36 PERSISTFILE_BASENAME = "PERSISTFILE_BASENAME" |
| 35 TEMP_BRANCH = "TEMP_BRANCH" | 37 TEMP_BRANCH = "TEMP_BRANCH" |
| 36 BRANCHNAME = "BRANCHNAME" | 38 BRANCHNAME = "BRANCHNAME" |
| 37 DOT_GIT_LOCATION = "DOT_GIT_LOCATION" | 39 DOT_GIT_LOCATION = "DOT_GIT_LOCATION" |
| 38 VERSION_FILE = "VERSION_FILE" | 40 VERSION_FILE = "VERSION_FILE" |
| 39 CHANGELOG_FILE = "CHANGELOG_FILE" | 41 CHANGELOG_FILE = "CHANGELOG_FILE" |
| 40 CHANGELOG_ENTRY_FILE = "CHANGELOG_ENTRY_FILE" | 42 CHANGELOG_ENTRY_FILE = "CHANGELOG_ENTRY_FILE" |
| 41 COMMITMSG_FILE = "COMMITMSG_FILE" | 43 COMMITMSG_FILE = "COMMITMSG_FILE" |
| 42 PATCH_FILE = "PATCH_FILE" | 44 PATCH_FILE = "PATCH_FILE" |
| (...skipping 17 matching lines...) Expand all Loading... |
| 60 | 62 |
| 61 def FileToText(file_name): | 63 def FileToText(file_name): |
| 62 with open(file_name) as f: | 64 with open(file_name) as f: |
| 63 return f.read() | 65 return f.read() |
| 64 | 66 |
| 65 | 67 |
| 66 def MSub(rexp, replacement, text): | 68 def MSub(rexp, replacement, text): |
| 67 return re.sub(rexp, replacement, text, flags=re.MULTILINE) | 69 return re.sub(rexp, replacement, text, flags=re.MULTILINE) |
| 68 | 70 |
| 69 | 71 |
| 72 def Fill80(line): |
| 73 # Replace tabs and remove surrounding space. |
| 74 line = re.sub(r"\t", r" ", line.strip()) |
| 75 |
| 76 # Format with 8 characters indentation and line width 80. |
| 77 return textwrap.fill(line, width=80, initial_indent=" ", |
| 78 subsequent_indent=" ") |
| 79 |
| 80 |
| 70 def GetLastChangeLogEntries(change_log_file): | 81 def GetLastChangeLogEntries(change_log_file): |
| 71 result = [] | 82 result = [] |
| 72 for line in LinesInFile(change_log_file): | 83 for line in LinesInFile(change_log_file): |
| 73 if re.search(r"^\d{4}-\d{2}-\d{2}:", line) and result: break | 84 if re.search(r"^\d{4}-\d{2}-\d{2}:", line) and result: break |
| 74 result.append(line) | 85 result.append(line) |
| 75 return "".join(result) | 86 return "".join(result) |
| 76 | 87 |
| 77 | 88 |
| 89 def MakeComment(text): |
| 90 return MSub(r"^( ?)", "#", text) |
| 91 |
| 92 |
| 93 def StripComments(text): |
| 94 # Use split not splitlines to keep terminal newlines. |
| 95 return "\n".join(filter(lambda x: not x.startswith("#"), text.split("\n"))) |
| 96 |
| 97 |
| 98 def MakeChangeLogBody(commit_messages, auto_format=False): |
| 99 result = "" |
| 100 added_titles = set() |
| 101 for (title, body, author) in commit_messages: |
| 102 # TODO(machenbach): Reload the commit description from rietveld in order to |
| 103 # catch late changes. |
| 104 title = title.strip() |
| 105 if auto_format: |
| 106 # Only add commits that set the LOG flag correctly. |
| 107 log_exp = r"^[ \t]*LOG[ \t]*=[ \t]*(?:Y(?:ES)?)|TRUE" |
| 108 if not re.search(log_exp, body, flags=re.I | re.M): |
| 109 continue |
| 110 # Never include reverts. |
| 111 if title.startswith("Revert "): |
| 112 continue |
| 113 # Don't include duplicates. |
| 114 if title in added_titles: |
| 115 continue |
| 116 |
| 117 # TODO(machenbach): Let python do all formatting. Get raw git title, attach |
| 118 # issue and add/move dot to the end - all in one line. Make formatting and |
| 119 # indentation afterwards. |
| 120 |
| 121 # Add the commit's title line. |
| 122 result += "%s\n" % Fill80(title) |
| 123 added_titles.add(title) |
| 124 |
| 125 # Add bug references. |
| 126 result += MakeChangeLogBugReference(body) |
| 127 |
| 128 # Append the commit's author for reference if not in auto-format mode. |
| 129 if not auto_format: |
| 130 result += "%s\n" % Fill80("(%s)" % author.strip()) |
| 131 |
| 132 result += "\n" |
| 133 return result |
| 134 |
| 135 |
| 136 def MakeChangeLogBugReference(body): |
| 137 """Grep for "BUG=xxxx" lines in the commit message and convert them to |
| 138 "(issue xxxx)". |
| 139 """ |
| 140 crbugs = [] |
| 141 v8bugs = [] |
| 142 |
| 143 def AddIssues(text): |
| 144 ref = re.match(r"^BUG[ \t]*=[ \t]*(.+)$", text.strip()) |
| 145 if not ref: |
| 146 return |
| 147 for bug in ref.group(1).split(","): |
| 148 bug = bug.strip() |
| 149 match = re.match(r"^v8:(\d+)$", bug) |
| 150 if match: v8bugs.append(int(match.group(1))) |
| 151 else: |
| 152 match = re.match(r"^(?:chromium:)?(\d+)$", bug) |
| 153 if match: crbugs.append(int(match.group(1))) |
| 154 |
| 155 # Add issues to crbugs and v8bugs. |
| 156 map(AddIssues, body.splitlines()) |
| 157 |
| 158 # Filter duplicates, sort, stringify. |
| 159 crbugs = map(str, sorted(set(crbugs))) |
| 160 v8bugs = map(str, sorted(set(v8bugs))) |
| 161 |
| 162 bug_groups = [] |
| 163 def FormatIssues(prefix, bugs): |
| 164 if len(bugs) > 0: |
| 165 plural = "s" if len(bugs) > 1 else "" |
| 166 bug_groups.append("%sissue%s %s" % (prefix, plural, ", ".join(bugs))) |
| 167 |
| 168 FormatIssues("", v8bugs) |
| 169 FormatIssues("Chromium ", crbugs) |
| 170 |
| 171 if len(bug_groups) > 0: |
| 172 # Format with 8 characters indentation and max 80 character lines. |
| 173 return "%s\n" % Fill80("(%s)" % ", ".join(bug_groups)) |
| 174 else: |
| 175 return "" |
| 176 |
| 177 |
| 78 # Some commands don't like the pipe, e.g. calling vi from within the script or | 178 # Some commands don't like the pipe, e.g. calling vi from within the script or |
| 79 # from subscripts like git cl upload. | 179 # from subscripts like git cl upload. |
| 80 def Command(cmd, args="", prefix="", pipe=True): | 180 def Command(cmd, args="", prefix="", pipe=True): |
| 81 cmd_line = "%s %s %s" % (prefix, cmd, args) | 181 cmd_line = "%s %s %s" % (prefix, cmd, args) |
| 82 print "Command: %s" % cmd_line | 182 print "Command: %s" % cmd_line |
| 83 try: | 183 try: |
| 84 if pipe: | 184 if pipe: |
| 85 return subprocess.check_output(cmd_line, shell=True) | 185 return subprocess.check_output(cmd_line, shell=True) |
| 86 else: | 186 else: |
| 87 return subprocess.check_call(cmd_line, shell=True) | 187 return subprocess.check_call(cmd_line, shell=True) |
| 88 except subprocess.CalledProcessError: | 188 except subprocess.CalledProcessError: |
| 89 return None | 189 return None |
| 90 | 190 |
| 91 | 191 |
| 92 # Wrapper for side effects. | 192 # Wrapper for side effects. |
| 93 class SideEffectHandler(object): | 193 class SideEffectHandler(object): |
| 94 def Command(self, cmd, args="", prefix="", pipe=True): | 194 def Command(self, cmd, args="", prefix="", pipe=True): |
| 95 return Command(cmd, args, prefix, pipe) | 195 return Command(cmd, args, prefix, pipe) |
| 96 | 196 |
| 97 def ReadLine(self): | 197 def ReadLine(self): |
| 98 return sys.stdin.readline().strip() | 198 return sys.stdin.readline().strip() |
| 99 | 199 |
| 200 def ReadURL(self, url): |
| 201 # pylint: disable=E1121 |
| 202 url_fh = urllib2.urlopen(url, None, 60) |
| 203 try: |
| 204 return url_fh.read() |
| 205 finally: |
| 206 url_fh.close() |
| 207 |
| 100 DEFAULT_SIDE_EFFECT_HANDLER = SideEffectHandler() | 208 DEFAULT_SIDE_EFFECT_HANDLER = SideEffectHandler() |
| 101 | 209 |
| 102 | 210 |
| 103 class Step(object): | 211 class Step(object): |
| 104 def __init__(self, text="", requires=None): | 212 def __init__(self, text, requires, number, config, state, options, handler): |
| 105 self._text = text | 213 self._text = text |
| 106 self._number = -1 | |
| 107 self._requires = requires | 214 self._requires = requires |
| 108 self._side_effect_handler = DEFAULT_SIDE_EFFECT_HANDLER | |
| 109 | |
| 110 def SetNumber(self, number): | |
| 111 self._number = number | 215 self._number = number |
| 112 | |
| 113 def SetConfig(self, config): | |
| 114 self._config = config | 216 self._config = config |
| 115 | |
| 116 def SetState(self, state): | |
| 117 self._state = state | 217 self._state = state |
| 118 | |
| 119 def SetOptions(self, options): | |
| 120 self._options = options | 218 self._options = options |
| 121 | |
| 122 def SetSideEffectHandler(self, handler): | |
| 123 self._side_effect_handler = handler | 219 self._side_effect_handler = handler |
| 220 assert self._number >= 0 |
| 221 assert self._config is not None |
| 222 assert self._state is not None |
| 223 assert self._side_effect_handler is not None |
| 124 | 224 |
| 125 def Config(self, key): | 225 def Config(self, key): |
| 126 return self._config[key] | 226 return self._config[key] |
| 127 | 227 |
| 128 def Run(self): | 228 def Run(self): |
| 129 assert self._number >= 0 | |
| 130 assert self._config is not None | |
| 131 assert self._state is not None | |
| 132 assert self._side_effect_handler is not None | |
| 133 if self._requires: | 229 if self._requires: |
| 134 self.RestoreIfUnset(self._requires) | 230 self.RestoreIfUnset(self._requires) |
| 135 if not self._state[self._requires]: | 231 if not self._state[self._requires]: |
| 136 return | 232 return |
| 137 print ">>> Step %d: %s" % (self._number, self._text) | 233 print ">>> Step %d: %s" % (self._number, self._text) |
| 138 self.RunStep() | 234 self.RunStep() |
| 139 | 235 |
| 140 def RunStep(self): | 236 def RunStep(self): |
| 141 raise NotImplementedError | 237 raise NotImplementedError |
| 142 | 238 |
| 143 def ReadLine(self): | 239 def ReadLine(self, default=None): |
| 144 return self._side_effect_handler.ReadLine() | 240 # Don't prompt in forced mode. |
| 241 if self._options and self._options.f and default is not None: |
| 242 print "%s (forced)" % default |
| 243 return default |
| 244 else: |
| 245 return self._side_effect_handler.ReadLine() |
| 145 | 246 |
| 146 def Git(self, args="", prefix="", pipe=True): | 247 def Git(self, args="", prefix="", pipe=True): |
| 147 return self._side_effect_handler.Command("git", args, prefix, pipe) | 248 return self._side_effect_handler.Command("git", args, prefix, pipe) |
| 148 | 249 |
| 149 def Editor(self, args): | 250 def Editor(self, args): |
| 150 return self._side_effect_handler.Command(os.environ["EDITOR"], args, | 251 return self._side_effect_handler.Command(os.environ["EDITOR"], args, |
| 151 pipe=False) | 252 pipe=False) |
| 152 | 253 |
| 254 def ReadURL(self, url): |
| 255 return self._side_effect_handler.ReadURL(url) |
| 256 |
| 153 def Die(self, msg=""): | 257 def Die(self, msg=""): |
| 154 if msg != "": | 258 if msg != "": |
| 155 print "Error: %s" % msg | 259 print "Error: %s" % msg |
| 156 print "Exiting" | 260 print "Exiting" |
| 157 raise Exception(msg) | 261 raise Exception(msg) |
| 158 | 262 |
| 263 def DieInForcedMode(self, msg=""): |
| 264 if self._options and self._options.f: |
| 265 msg = msg or "Not implemented in forced mode." |
| 266 self.Die(msg) |
| 267 |
| 159 def Confirm(self, msg): | 268 def Confirm(self, msg): |
| 160 print "%s [Y/n] " % msg, | 269 print "%s [Y/n] " % msg, |
| 161 answer = self.ReadLine() | 270 answer = self.ReadLine(default="Y") |
| 162 return answer == "" or answer == "Y" or answer == "y" | 271 return answer == "" or answer == "Y" or answer == "y" |
| 163 | 272 |
| 164 def DeleteBranch(self, name): | 273 def DeleteBranch(self, name): |
| 165 git_result = self.Git("branch").strip() | 274 git_result = self.Git("branch").strip() |
| 166 for line in git_result.splitlines(): | 275 for line in git_result.splitlines(): |
| 167 if re.match(r".*\s+%s$" % name, line): | 276 if re.match(r".*\s+%s$" % name, line): |
| 168 msg = "Branch %s exists, do you want to delete it?" % name | 277 msg = "Branch %s exists, do you want to delete it?" % name |
| 169 if self.Confirm(msg): | 278 if self.Confirm(msg): |
| 170 if self.Git("branch -D %s" % name) is None: | 279 if self.Git("branch -D %s" % name) is None: |
| 171 self.Die("Deleting branch '%s' failed." % name) | 280 self.Die("Deleting branch '%s' failed." % name) |
| (...skipping 13 matching lines...) Expand all Loading... |
| 185 | 294 |
| 186 def RestoreIfUnset(self, var_name): | 295 def RestoreIfUnset(self, var_name): |
| 187 if self._state.get(var_name) is None: | 296 if self._state.get(var_name) is None: |
| 188 self._state[var_name] = self.Restore(var_name) | 297 self._state[var_name] = self.Restore(var_name) |
| 189 | 298 |
| 190 def InitialEnvironmentChecks(self): | 299 def InitialEnvironmentChecks(self): |
| 191 # Cancel if this is not a git checkout. | 300 # Cancel if this is not a git checkout. |
| 192 if not os.path.exists(self._config[DOT_GIT_LOCATION]): | 301 if not os.path.exists(self._config[DOT_GIT_LOCATION]): |
| 193 self.Die("This is not a git checkout, this script won't work for you.") | 302 self.Die("This is not a git checkout, this script won't work for you.") |
| 194 | 303 |
| 304 # TODO(machenbach): Don't use EDITOR in forced mode as soon as script is |
| 305 # well tested. |
| 195 # Cancel if EDITOR is unset or not executable. | 306 # Cancel if EDITOR is unset or not executable. |
| 196 if (not os.environ.get("EDITOR") or | 307 if (not os.environ.get("EDITOR") or |
| 197 Command("which", os.environ["EDITOR"]) is None): | 308 Command("which", os.environ["EDITOR"]) is None): |
| 198 self.Die("Please set your EDITOR environment variable, you'll need it.") | 309 self.Die("Please set your EDITOR environment variable, you'll need it.") |
| 199 | 310 |
| 200 def CommonPrepare(self): | 311 def CommonPrepare(self): |
| 201 # Check for a clean workdir. | 312 # Check for a clean workdir. |
| 202 if self.Git("status -s -uno").strip() != "": | 313 if self.Git("status -s -uno").strip() != "": |
| 203 self.Die("Workspace is not clean. Please commit or undo your changes.") | 314 self.Die("Workspace is not clean. Please commit or undo your changes.") |
| 204 | 315 |
| 205 # Persist current branch. | 316 # Persist current branch. |
| 206 current_branch = "" | 317 current_branch = "" |
| 207 git_result = self.Git("status -s -b -uno").strip() | 318 git_result = self.Git("status -s -b -uno").strip() |
| 208 for line in git_result.splitlines(): | 319 for line in git_result.splitlines(): |
| 209 match = re.match(r"^## (.+)", line) | 320 match = re.match(r"^## (.+)", line) |
| 210 if match: | 321 if match: |
| 211 current_branch = match.group(1) | 322 current_branch = match.group(1) |
| 212 break | 323 break |
| 213 self.Persist("current_branch", current_branch) | 324 self.Persist("current_branch", current_branch) |
| 214 | 325 |
| 215 # Fetch unfetched revisions. | 326 # Fetch unfetched revisions. |
| 216 if self.Git("svn fetch") is None: | 327 if self.Git("svn fetch") is None: |
| 217 self.Die("'git svn fetch' failed.") | 328 self.Die("'git svn fetch' failed.") |
| 218 | 329 |
| 330 def PrepareBranch(self): |
| 219 # Get ahold of a safe temporary branch and check it out. | 331 # Get ahold of a safe temporary branch and check it out. |
| 220 if current_branch != self._config[TEMP_BRANCH]: | 332 self.RestoreIfUnset("current_branch") |
| 333 if self._state["current_branch"] != self._config[TEMP_BRANCH]: |
| 221 self.DeleteBranch(self._config[TEMP_BRANCH]) | 334 self.DeleteBranch(self._config[TEMP_BRANCH]) |
| 222 self.Git("checkout -b %s" % self._config[TEMP_BRANCH]) | 335 self.Git("checkout -b %s" % self._config[TEMP_BRANCH]) |
| 223 | 336 |
| 224 # Delete the branch that will be created later if it exists already. | 337 # Delete the branch that will be created later if it exists already. |
| 225 self.DeleteBranch(self._config[BRANCHNAME]) | 338 self.DeleteBranch(self._config[BRANCHNAME]) |
| 226 | 339 |
| 227 def CommonCleanup(self): | 340 def CommonCleanup(self): |
| 228 self.RestoreIfUnset("current_branch") | 341 self.RestoreIfUnset("current_branch") |
| 229 self.Git("checkout -f %s" % self._state["current_branch"]) | 342 self.Git("checkout -f %s" % self._state["current_branch"]) |
| 230 if self._config[TEMP_BRANCH] != self._state["current_branch"]: | 343 if self._config[TEMP_BRANCH] != self._state["current_branch"]: |
| (...skipping 23 matching lines...) Expand all Loading... |
| 254 self.RestoreIfUnset("%s%s" % (prefix, v)) | 367 self.RestoreIfUnset("%s%s" % (prefix, v)) |
| 255 | 368 |
| 256 def WaitForLGTM(self): | 369 def WaitForLGTM(self): |
| 257 print ("Please wait for an LGTM, then type \"LGTM<Return>\" to commit " | 370 print ("Please wait for an LGTM, then type \"LGTM<Return>\" to commit " |
| 258 "your change. (If you need to iterate on the patch or double check " | 371 "your change. (If you need to iterate on the patch or double check " |
| 259 "that it's sane, do so in another shell, but remember to not " | 372 "that it's sane, do so in another shell, but remember to not " |
| 260 "change the headline of the uploaded CL.") | 373 "change the headline of the uploaded CL.") |
| 261 answer = "" | 374 answer = "" |
| 262 while answer != "LGTM": | 375 while answer != "LGTM": |
| 263 print "> ", | 376 print "> ", |
| 377 # TODO(machenbach): Add default="LGTM" to avoid prompt when script is |
| 378 # well tested and when prepare push cl has TBR flag. |
| 264 answer = self.ReadLine() | 379 answer = self.ReadLine() |
| 265 if answer != "LGTM": | 380 if answer != "LGTM": |
| 266 print "That was not 'LGTM'." | 381 print "That was not 'LGTM'." |
| 267 | 382 |
| 268 def WaitForResolvingConflicts(self, patch_file): | 383 def WaitForResolvingConflicts(self, patch_file): |
| 269 print("Applying the patch \"%s\" failed. Either type \"ABORT<Return>\", " | 384 print("Applying the patch \"%s\" failed. Either type \"ABORT<Return>\", " |
| 270 "or resolve the conflicts, stage *all* touched files with " | 385 "or resolve the conflicts, stage *all* touched files with " |
| 271 "'git add', and type \"RESOLVED<Return>\"") | 386 "'git add', and type \"RESOLVED<Return>\"") |
| 387 self.DieInForcedMode() |
| 272 answer = "" | 388 answer = "" |
| 273 while answer != "RESOLVED": | 389 while answer != "RESOLVED": |
| 274 if answer == "ABORT": | 390 if answer == "ABORT": |
| 275 self.Die("Applying the patch failed.") | 391 self.Die("Applying the patch failed.") |
| 276 if answer != "": | 392 if answer != "": |
| 277 print "That was not 'RESOLVED' or 'ABORT'." | 393 print "That was not 'RESOLVED' or 'ABORT'." |
| 278 print "> ", | 394 print "> ", |
| 279 answer = self.ReadLine() | 395 answer = self.ReadLine() |
| 280 | 396 |
| 281 # Takes a file containing the patch to apply as first argument. | 397 # Takes a file containing the patch to apply as first argument. |
| 282 def ApplyPatch(self, patch_file, reverse_patch=""): | 398 def ApplyPatch(self, patch_file, reverse_patch=""): |
| 283 args = "apply --index --reject %s \"%s\"" % (reverse_patch, patch_file) | 399 args = "apply --index --reject %s \"%s\"" % (reverse_patch, patch_file) |
| 284 if self.Git(args) is None: | 400 if self.Git(args) is None: |
| 285 self.WaitForResolvingConflicts(patch_file) | 401 self.WaitForResolvingConflicts(patch_file) |
| 286 | 402 |
| 287 | 403 |
| 288 class UploadStep(Step): | 404 class UploadStep(Step): |
| 289 def __init__(self): | 405 MESSAGE = "Upload for code review." |
| 290 Step.__init__(self, "Upload for code review.") | |
| 291 | 406 |
| 292 def RunStep(self): | 407 def RunStep(self): |
| 293 print "Please enter the email address of a V8 reviewer for your patch: ", | 408 if self._options.r: |
| 294 reviewer = self.ReadLine() | 409 print "Using account %s for review." % self._options.r |
| 295 args = "cl upload -r \"%s\" --send-mail" % reviewer | 410 reviewer = self._options.r |
| 296 if self.Git(args,pipe=False) is None: | 411 else: |
| 412 print "Please enter the email address of a V8 reviewer for your patch: ", |
| 413 self.DieInForcedMode("A reviewer must be specified in forced mode.") |
| 414 reviewer = self.ReadLine() |
| 415 force_flag = " -f" if self._options.f else "" |
| 416 args = "cl upload -r \"%s\" --send-mail%s" % (reviewer, force_flag) |
| 417 # TODO(machenbach): Check output in forced mode. Verify that all required |
| 418 # base files were uploaded, if not retry. |
| 419 if self.Git(args, pipe=False) is None: |
| 297 self.Die("'git cl upload' failed, please try again.") | 420 self.Die("'git cl upload' failed, please try again.") |
| 421 |
| 422 |
| 423 def MakeStep(step_class=Step, number=0, state=None, config=None, |
| 424 options=None, side_effect_handler=DEFAULT_SIDE_EFFECT_HANDLER): |
| 425 # Allow to pass in empty dictionaries. |
| 426 state = state if state is not None else {} |
| 427 config = config if config is not None else {} |
| 428 |
| 429 try: |
| 430 message = step_class.MESSAGE |
| 431 except AttributeError: |
| 432 message = step_class.__name__ |
| 433 try: |
| 434 requires = step_class.REQUIRES |
| 435 except AttributeError: |
| 436 requires = None |
| 437 |
| 438 return step_class(message, requires, number=number, config=config, |
| 439 state=state, options=options, |
| 440 handler=side_effect_handler) |
| 441 |
| 442 |
| 443 def RunScript(step_classes, |
| 444 config, |
| 445 options, |
| 446 side_effect_handler=DEFAULT_SIDE_EFFECT_HANDLER): |
| 447 state = {} |
| 448 steps = [] |
| 449 for (number, step_class) in enumerate(step_classes): |
| 450 steps.append(MakeStep(step_class, number, state, config, |
| 451 options, side_effect_handler)) |
| 452 |
| 453 for step in steps[options.s:]: |
| 454 step.Run() |
| OLD | NEW |