1b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch#!/usr/bin/env python
2b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch# Copyright 2013 the V8 project authors. All rights reserved.
3b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch# Redistribution and use in source and binary forms, with or without
4b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch# modification, are permitted provided that the following conditions are
5b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch# met:
6b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch#
7b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch#     * Redistributions of source code must retain the above copyright
8b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch#       notice, this list of conditions and the following disclaimer.
9b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch#     * Redistributions in binary form must reproduce the above
10b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch#       copyright notice, this list of conditions and the following
11b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch#       disclaimer in the documentation and/or other materials provided
12b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch#       with the distribution.
13b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch#     * Neither the name of Google Inc. nor the names of its
14b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch#       contributors may be used to endorse or promote products derived
15b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch#       from this software without specific prior written permission.
16b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch#
17b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
29b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochimport argparse
30b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochimport datetime
31b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochimport httplib
32b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochimport glob
33b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochimport imp
34b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochimport json
35b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochimport os
36b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochimport re
37b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochimport shutil
38b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochimport subprocess
39b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochimport sys
40b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochimport textwrap
41b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochimport time
42b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochimport urllib
43b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochimport urllib2
44b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
45b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochfrom git_recipes import GitRecipesMixin
46b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochfrom git_recipes import GitFailedException
47b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
48958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily BernierCHANGELOG_FILE = "ChangeLog"
49014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben MurdochDAY_IN_SECONDS = 24 * 60 * 60
50014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben MurdochPUSH_MSG_GIT_RE = re.compile(r".* \(based on (?P<git_rev>[a-fA-F0-9]+)\)$")
51014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben MurdochPUSH_MSG_NEW_RE = re.compile(r"^Version \d+\.\d+\.\d+$")
52014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben MurdochVERSION_FILE = os.path.join("include", "v8-version.h")
53342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen MurdochWATCHLISTS_FILE = "WATCHLISTS"
54b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
55b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch# V8 base directory.
56958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily BernierV8_BASE = os.path.dirname(
57b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
58b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
59b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
60b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochdef TextToFile(text, file_name):
61b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  with open(file_name, "w") as f:
62b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    f.write(text)
63b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
64b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
65b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochdef AppendToFile(text, file_name):
66b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  with open(file_name, "a") as f:
67b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    f.write(text)
68b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
69b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
70b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochdef LinesInFile(file_name):
71b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  with open(file_name) as f:
72b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    for line in f:
73b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      yield line
74b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
75b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
76b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochdef FileToText(file_name):
77b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  with open(file_name) as f:
78b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    return f.read()
79b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
80b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
81b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochdef MSub(rexp, replacement, text):
82b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  return re.sub(rexp, replacement, text, flags=re.MULTILINE)
83b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
84b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
85b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochdef Fill80(line):
86b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  # Replace tabs and remove surrounding space.
87b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  line = re.sub(r"\t", r"        ", line.strip())
88b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
89b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  # Format with 8 characters indentation and line width 80.
90b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  return textwrap.fill(line, width=80, initial_indent="        ",
91b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch                       subsequent_indent="        ")
92b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
93b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
94b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochdef MakeComment(text):
95b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  return MSub(r"^( ?)", "#", text)
96b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
97b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
98b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochdef StripComments(text):
99b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  # Use split not splitlines to keep terminal newlines.
100b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  return "\n".join(filter(lambda x: not x.startswith("#"), text.split("\n")))
101b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
102b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
103b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochdef MakeChangeLogBody(commit_messages, auto_format=False):
104b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  result = ""
105b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  added_titles = set()
106b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  for (title, body, author) in commit_messages:
107b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    # TODO(machenbach): Better check for reverts. A revert should remove the
108b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    # original CL from the actual log entry.
109b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    title = title.strip()
110b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    if auto_format:
111b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      # Only add commits that set the LOG flag correctly.
112b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      log_exp = r"^[ \t]*LOG[ \t]*=[ \t]*(?:(?:Y(?:ES)?)|TRUE)"
113b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      if not re.search(log_exp, body, flags=re.I | re.M):
114b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        continue
115b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      # Never include reverts.
116b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      if title.startswith("Revert "):
117b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        continue
118b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      # Don't include duplicates.
119b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      if title in added_titles:
120b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        continue
121b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
122b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    # Add and format the commit's title and bug reference. Move dot to the end.
123b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    added_titles.add(title)
124b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    raw_title = re.sub(r"(\.|\?|!)$", "", title)
125b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    bug_reference = MakeChangeLogBugReference(body)
126b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    space = " " if bug_reference else ""
127b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    result += "%s\n" % Fill80("%s%s%s." % (raw_title, space, bug_reference))
128b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
129b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    # Append the commit's author for reference if not in auto-format mode.
130b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    if not auto_format:
131b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      result += "%s\n" % Fill80("(%s)" % author.strip())
132b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
133b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    result += "\n"
134b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  return result
135b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
136b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
137b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochdef MakeChangeLogBugReference(body):
138b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  """Grep for "BUG=xxxx" lines in the commit message and convert them to
139b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  "(issue xxxx)".
140b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  """
141b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  crbugs = []
142b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  v8bugs = []
143b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
144b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  def AddIssues(text):
145b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    ref = re.match(r"^BUG[ \t]*=[ \t]*(.+)$", text.strip())
146b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    if not ref:
147b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      return
148b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    for bug in ref.group(1).split(","):
149b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      bug = bug.strip()
150b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      match = re.match(r"^v8:(\d+)$", bug)
151b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      if match: v8bugs.append(int(match.group(1)))
152b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      else:
153b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        match = re.match(r"^(?:chromium:)?(\d+)$", bug)
154b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        if match: crbugs.append(int(match.group(1)))
155b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
156b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  # Add issues to crbugs and v8bugs.
157b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  map(AddIssues, body.splitlines())
158b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
159b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  # Filter duplicates, sort, stringify.
160b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  crbugs = map(str, sorted(set(crbugs)))
161b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  v8bugs = map(str, sorted(set(v8bugs)))
162b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
163b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  bug_groups = []
164b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  def FormatIssues(prefix, bugs):
165b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    if len(bugs) > 0:
166b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      plural = "s" if len(bugs) > 1 else ""
167b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      bug_groups.append("%sissue%s %s" % (prefix, plural, ", ".join(bugs)))
168b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
169b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  FormatIssues("", v8bugs)
170b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  FormatIssues("Chromium ", crbugs)
171b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
172b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  if len(bug_groups) > 0:
173b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    return "(%s)" % ", ".join(bug_groups)
174b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  else:
175b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    return ""
176b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
177b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
178b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochdef SortingKey(version):
179b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  """Key for sorting version number strings: '3.11' > '3.2.1.1'"""
180b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  version_keys = map(int, version.split("."))
181b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  # Fill up to full version numbers to normalize comparison.
182b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  while len(version_keys) < 4:  # pragma: no cover
183b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    version_keys.append(0)
184b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  # Fill digits.
185b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  return ".".join(map("{0:04d}".format, version_keys))
186b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
187b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
188b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch# Some commands don't like the pipe, e.g. calling vi from within the script or
189b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch# from subscripts like git cl upload.
190b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochdef Command(cmd, args="", prefix="", pipe=True, cwd=None):
191b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  cwd = cwd or os.getcwd()
192b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  # TODO(machenbach): Use timeout.
193b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  cmd_line = "%s %s %s" % (prefix, cmd, args)
194b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  print "Command: %s" % cmd_line
195b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  print "in %s" % cwd
196b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  sys.stdout.flush()
197b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  try:
198b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    if pipe:
199b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      return subprocess.check_output(cmd_line, shell=True, cwd=cwd)
200b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    else:
201b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      return subprocess.check_call(cmd_line, shell=True, cwd=cwd)
202b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  except subprocess.CalledProcessError:
203b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    return None
204b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  finally:
205b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    sys.stdout.flush()
206b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    sys.stderr.flush()
207b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
208b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
209014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdochdef SanitizeVersionTag(tag):
210014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    version_without_prefix = re.compile(r"^\d+\.\d+\.\d+(?:\.\d+)?$")
211014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    version_with_prefix = re.compile(r"^tags\/\d+\.\d+\.\d+(?:\.\d+)?$")
212014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
213014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    if version_without_prefix.match(tag):
214014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      return tag
215014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    elif version_with_prefix.match(tag):
216014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch        return tag[len("tags/"):]
217014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    else:
218014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      return None
219014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
220014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
221014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdochdef NormalizeVersionTags(version_tags):
222014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  normalized_version_tags = []
223014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
224014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  # Remove tags/ prefix because of packed refs.
225014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  for current_tag in version_tags:
226014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    version_tag = SanitizeVersionTag(current_tag)
227014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    if version_tag != None:
228014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      normalized_version_tags.append(version_tag)
229014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
230014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  return normalized_version_tags
231014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
232014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
233b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch# Wrapper for side effects.
234b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochclass SideEffectHandler(object):  # pragma: no cover
235b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  def Call(self, fun, *args, **kwargs):
236b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    return fun(*args, **kwargs)
237b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
238b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  def Command(self, cmd, args="", prefix="", pipe=True, cwd=None):
239b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    return Command(cmd, args, prefix, pipe, cwd=cwd)
240b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
241b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  def ReadLine(self):
242b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    return sys.stdin.readline().strip()
243b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
244b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  def ReadURL(self, url, params=None):
245b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    # pylint: disable=E1121
246b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    url_fh = urllib2.urlopen(url, params, 60)
247b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    try:
248b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      return url_fh.read()
249b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    finally:
250b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      url_fh.close()
251b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
252b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  def ReadClusterFuzzAPI(self, api_key, **params):
253b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    params["api_key"] = api_key.strip()
254b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    params = urllib.urlencode(params)
255b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
256b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    headers = {"Content-type": "application/x-www-form-urlencoded"}
257b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
258b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    conn = httplib.HTTPSConnection("backend-dot-cluster-fuzz.appspot.com")
259b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    conn.request("POST", "/_api/", params, headers)
260b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
261b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    response = conn.getresponse()
262b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    data = response.read()
263b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
264b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    try:
265b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      return json.loads(data)
266b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    except:
267b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      print data
268b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      print "ERROR: Could not read response. Is your key valid?"
269b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      raise
270b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
271b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  def Sleep(self, seconds):
272b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    time.sleep(seconds)
273b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
274b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  def GetDate(self):
275b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    return datetime.date.today().strftime("%Y-%m-%d")
276b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
277b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  def GetUTCStamp(self):
278b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    return time.mktime(datetime.datetime.utcnow().timetuple())
279b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
280b8a8cc1952d61a2f3a2568848933943a543b5d3eBen MurdochDEFAULT_SIDE_EFFECT_HANDLER = SideEffectHandler()
281b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
282b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
283b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochclass NoRetryException(Exception):
284b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  pass
285b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
286b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
287958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernierclass VCInterface(object):
288958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier  def InjectStep(self, step):
289958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier    self.step=step
290958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier
291958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier  def Pull(self):
292958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier    raise NotImplementedError()
293958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier
294958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier  def Fetch(self):
295958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier    raise NotImplementedError()
296958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier
297958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier  def GetTags(self):
298958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier    raise NotImplementedError()
299958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier
300958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier  def GetBranches(self):
301958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier    raise NotImplementedError()
302958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier
303958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier  def MasterBranch(self):
304958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier    raise NotImplementedError()
305958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier
306958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier  def CandidateBranch(self):
307958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier    raise NotImplementedError()
308958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier
309958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier  def RemoteMasterBranch(self):
310958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier    raise NotImplementedError()
311958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier
312958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier  def RemoteCandidateBranch(self):
313958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier    raise NotImplementedError()
314958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier
315958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier  def RemoteBranch(self, name):
316958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier    raise NotImplementedError()
317958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier
318958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier  def CLLand(self):
319958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier    raise NotImplementedError()
320958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier
321958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier  def Tag(self, tag, remote, message):
322958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier    """Sets a tag for the current commit.
323958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier
324958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier    Assumptions: The commit already landed and the commit message is unique.
325958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier    """
326958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier    raise NotImplementedError()
327958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier
328958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier
329958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernierclass GitInterface(VCInterface):
330958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier  def Pull(self):
331958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier    self.step.GitPull()
332958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier
333958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier  def Fetch(self):
334958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier    self.step.Git("fetch")
335958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier
336958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier  def GetTags(self):
337958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier     return self.step.Git("tag").strip().splitlines()
338958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier
339958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier  def GetBranches(self):
340958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier    # Get relevant remote branches, e.g. "branch-heads/3.25".
341958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier    branches = filter(
342958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier        lambda s: re.match(r"^branch\-heads/\d+\.\d+$", s),
343958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier        self.step.GitRemotes())
344958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier    # Remove 'branch-heads/' prefix.
345958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier    return map(lambda s: s[13:], branches)
346958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier
347958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier  def MasterBranch(self):
348958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier    return "master"
349958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier
350958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier  def CandidateBranch(self):
351958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier    return "candidates"
352958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier
353958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier  def RemoteMasterBranch(self):
354958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier    return "origin/master"
355958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier
356958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier  def RemoteCandidateBranch(self):
357958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier    return "origin/candidates"
358958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier
359958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier  def RemoteBranch(self, name):
360014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    # Assume that if someone "fully qualified" the ref, they know what they
361014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    # want.
362014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    if name.startswith('refs/'):
363014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      return name
364958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier    if name in ["candidates", "master"]:
365014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      return "refs/remotes/origin/%s" % name
366014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    try:
367014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      # Check if branch is in heads.
368014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      if self.step.Git("show-ref refs/remotes/origin/%s" % name).strip():
369014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch        return "refs/remotes/origin/%s" % name
370014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    except GitFailedException:
371014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      pass
372014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    try:
373014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      # Check if branch is in branch-heads.
374014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      if self.step.Git("show-ref refs/remotes/branch-heads/%s" % name).strip():
375014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch        return "refs/remotes/branch-heads/%s" % name
376014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    except GitFailedException:
377014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      pass
378014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    self.Die("Can't find remote of %s" % name)
379958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier
380958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier  def Tag(self, tag, remote, message):
381958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier    # Wait for the commit to appear. Assumes unique commit message titles (this
382958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier    # is the case for all automated merge and push commits - also no title is
383958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier    # the prefix of another title).
384958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier    commit = None
3851b268ca467c924004286c97bac133db489cf43d0Ben Murdoch    for wait_interval in [10, 30, 60, 60, 60, 60, 60]:
386958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier      self.step.Git("fetch")
387958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier      commit = self.step.GitLog(n=1, format="%H", grep=message, branch=remote)
388958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier      if commit:
389958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier        break
390958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier      print("The commit has not replicated to git. Waiting for %s seconds." %
391958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier            wait_interval)
392958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier      self.step._side_effect_handler.Sleep(wait_interval)
393958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier    else:
394958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier      self.step.Die("Couldn't determine commit for setting the tag. Maybe the "
395958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier                    "git updater is lagging behind?")
396958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier
397958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier    self.step.Git("tag %s %s" % (tag, commit))
398958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier    self.step.Git("push origin %s" % tag)
399958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier
400958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier  def CLLand(self):
401958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier    self.step.GitCLLand()
402958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier
403958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier
404b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochclass Step(GitRecipesMixin):
405b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  def __init__(self, text, number, config, state, options, handler):
406b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    self._text = text
407b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    self._number = number
408b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    self._config = config
409b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    self._state = state
410b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    self._options = options
411b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    self._side_effect_handler = handler
412958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier    self.vc = GitInterface()
413958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier    self.vc.InjectStep(self)
414b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
415b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    # The testing configuration might set a different default cwd.
416958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier    self.default_cwd = (self._config.get("DEFAULT_CWD") or
417958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier                        os.path.join(self._options.work_dir, "v8"))
418b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
419b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    assert self._number >= 0
420b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    assert self._config is not None
421b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    assert self._state is not None
422b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    assert self._side_effect_handler is not None
423b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
424b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  def __getitem__(self, key):
425b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    # Convenience method to allow direct [] access on step classes for
426b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    # manipulating the backed state dict.
427014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    return self._state.get(key)
428b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
429b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  def __setitem__(self, key, value):
430b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    # Convenience method to allow direct [] access on step classes for
431b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    # manipulating the backed state dict.
432b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    self._state[key] = value
433b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
434b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  def Config(self, key):
435b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    return self._config[key]
436b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
437b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  def Run(self):
438b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    # Restore state.
439b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    state_file = "%s-state.json" % self._config["PERSISTFILE_BASENAME"]
440b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    if not self._state and os.path.exists(state_file):
441b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      self._state.update(json.loads(FileToText(state_file)))
442b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
443b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    print ">>> Step %d: %s" % (self._number, self._text)
444b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    try:
445b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      return self.RunStep()
446b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    finally:
447b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      # Persist state.
448b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      TextToFile(json.dumps(self._state), state_file)
449b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
450b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  def RunStep(self):  # pragma: no cover
451b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    raise NotImplementedError
452b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
453b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  def Retry(self, cb, retry_on=None, wait_plan=None):
454b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    """ Retry a function.
455b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    Params:
456b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      cb: The function to retry.
457b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      retry_on: A callback that takes the result of the function and returns
458b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch                True if the function should be retried. A function throwing an
459b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch                exception is always retried.
460b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      wait_plan: A list of waiting delays between retries in seconds. The
461b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch                 maximum number of retries is len(wait_plan).
462b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    """
463b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    retry_on = retry_on or (lambda x: False)
464b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    wait_plan = list(wait_plan or [])
465b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    wait_plan.reverse()
466b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    while True:
467b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      got_exception = False
468b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      try:
469b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        result = cb()
470b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      except NoRetryException as e:
471b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        raise e
472b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      except Exception as e:
473b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        got_exception = e
474b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      if got_exception or retry_on(result):
475b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        if not wait_plan:  # pragma: no cover
476b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch          raise Exception("Retried too often. Giving up. Reason: %s" %
477b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch                          str(got_exception))
478b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        wait_time = wait_plan.pop()
479b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        print "Waiting for %f seconds." % wait_time
480b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        self._side_effect_handler.Sleep(wait_time)
481b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        print "Retrying..."
482b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      else:
483b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        return result
484b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
485b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  def ReadLine(self, default=None):
486b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    # Don't prompt in forced mode.
487b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    if self._options.force_readline_defaults and default is not None:
488b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      print "%s (forced)" % default
489b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      return default
490b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    else:
491b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      return self._side_effect_handler.ReadLine()
492b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
493b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  def Command(self, name, args, cwd=None):
494b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    cmd = lambda: self._side_effect_handler.Command(
495b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        name, args, "", True, cwd=cwd or self.default_cwd)
496b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    return self.Retry(cmd, None, [5])
497b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
498b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  def Git(self, args="", prefix="", pipe=True, retry_on=None, cwd=None):
499b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    cmd = lambda: self._side_effect_handler.Command(
500b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        "git", args, prefix, pipe, cwd=cwd or self.default_cwd)
501b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    result = self.Retry(cmd, retry_on, [5, 30])
502b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    if result is None:
503b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      raise GitFailedException("'git %s' failed." % args)
504b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    return result
505b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
506b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  def Editor(self, args):
507b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    if self._options.requires_editor:
508b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      return self._side_effect_handler.Command(
509b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch          os.environ["EDITOR"],
510b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch          args,
511b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch          pipe=False,
512b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch          cwd=self.default_cwd)
513b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
514b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  def ReadURL(self, url, params=None, retry_on=None, wait_plan=None):
515b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    wait_plan = wait_plan or [3, 60, 600]
516b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    cmd = lambda: self._side_effect_handler.ReadURL(url, params)
517b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    return self.Retry(cmd, retry_on, wait_plan)
518b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
519b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  def GetDate(self):
520b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    return self._side_effect_handler.GetDate()
521b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
522b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  def Die(self, msg=""):
523b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    if msg != "":
524b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      print "Error: %s" % msg
525b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    print "Exiting"
526b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    raise Exception(msg)
527b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
528b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  def DieNoManualMode(self, msg=""):
529b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    if not self._options.manual:  # pragma: no cover
530b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      msg = msg or "Only available in manual mode."
531b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      self.Die(msg)
532b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
533b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  def Confirm(self, msg):
534b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    print "%s [Y/n] " % msg,
535b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    answer = self.ReadLine(default="Y")
536b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    return answer == "" or answer == "Y" or answer == "y"
537b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
538014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  def DeleteBranch(self, name, cwd=None):
539014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    for line in self.GitBranch(cwd=cwd).splitlines():
540b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      if re.match(r"\*?\s*%s$" % re.escape(name), line):
541b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        msg = "Branch %s exists, do you want to delete it?" % name
542b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        if self.Confirm(msg):
543014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch          self.GitDeleteBranch(name, cwd=cwd)
544b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch          print "Branch %s deleted." % name
545b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        else:
546b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch          msg = "Can't continue. Please delete branch %s and try again." % name
547b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch          self.Die(msg)
548b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
549b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  def InitialEnvironmentChecks(self, cwd):
550b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    # Cancel if this is not a git checkout.
551b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    if not os.path.exists(os.path.join(cwd, ".git")):  # pragma: no cover
552b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      self.Die("This is not a git checkout, this script won't work for you.")
553b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
554b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    # Cancel if EDITOR is unset or not executable.
555b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    if (self._options.requires_editor and (not os.environ.get("EDITOR") or
556b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        self.Command(
557b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch            "which", os.environ["EDITOR"]) is None)):  # pragma: no cover
558b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      self.Die("Please set your EDITOR environment variable, you'll need it.")
559b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
560b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  def CommonPrepare(self):
561b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    # Check for a clean workdir.
562b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    if not self.GitIsWorkdirClean():  # pragma: no cover
563b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      self.Die("Workspace is not clean. Please commit or undo your changes.")
564b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
565014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    # Checkout master in case the script was left on a work branch.
566014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    self.GitCheckout('origin/master')
567b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
568b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    # Fetch unfetched revisions.
569958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier    self.vc.Fetch()
570b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
571b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  def PrepareBranch(self):
572b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    # Delete the branch that will be created later if it exists already.
573b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    self.DeleteBranch(self._config["BRANCHNAME"])
574b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
575b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  def CommonCleanup(self):
576014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    self.GitCheckout('origin/master')
577014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    self.GitDeleteBranch(self._config["BRANCHNAME"])
578b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
579b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    # Clean up all temporary files.
580b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    for f in glob.iglob("%s*" % self._config["PERSISTFILE_BASENAME"]):
581b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      if os.path.isfile(f):
582b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        os.remove(f)
583b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      if os.path.isdir(f):
584b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        shutil.rmtree(f)
585b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
586b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  def ReadAndPersistVersion(self, prefix=""):
587b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    def ReadAndPersist(var_name, def_name):
588b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      match = re.match(r"^#define %s\s+(\d*)" % def_name, line)
589b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      if match:
590b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        value = match.group(1)
591b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        self["%s%s" % (prefix, var_name)] = value
592b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    for line in LinesInFile(os.path.join(self.default_cwd, VERSION_FILE)):
593014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      for (var_name, def_name) in [("major", "V8_MAJOR_VERSION"),
594014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch                                   ("minor", "V8_MINOR_VERSION"),
595014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch                                   ("build", "V8_BUILD_NUMBER"),
596014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch                                   ("patch", "V8_PATCH_LEVEL")]:
597b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        ReadAndPersist(var_name, def_name)
598b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
599b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  def WaitForLGTM(self):
600b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    print ("Please wait for an LGTM, then type \"LGTM<Return>\" to commit "
601b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch           "your change. (If you need to iterate on the patch or double check "
602b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch           "that it's sane, do so in another shell, but remember to not "
603b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch           "change the headline of the uploaded CL.")
604b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    answer = ""
605b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    while answer != "LGTM":
606b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      print "> ",
607b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      answer = self.ReadLine(None if self._options.wait_for_lgtm else "LGTM")
608b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      if answer != "LGTM":
609b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        print "That was not 'LGTM'."
610b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
611b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  def WaitForResolvingConflicts(self, patch_file):
612b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    print("Applying the patch \"%s\" failed. Either type \"ABORT<Return>\", "
613b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch          "or resolve the conflicts, stage *all* touched files with "
614b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch          "'git add', and type \"RESOLVED<Return>\"")
615b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    self.DieNoManualMode()
616b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    answer = ""
617b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    while answer != "RESOLVED":
618b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      if answer == "ABORT":
619b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        self.Die("Applying the patch failed.")
620b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      if answer != "":
621b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        print "That was not 'RESOLVED' or 'ABORT'."
622b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      print "> ",
623b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      answer = self.ReadLine()
624b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
625b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  # Takes a file containing the patch to apply as first argument.
626b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  def ApplyPatch(self, patch_file, revert=False):
627b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    try:
628b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      self.GitApplyPatch(patch_file, revert)
629b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    except GitFailedException:
630b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      self.WaitForResolvingConflicts(patch_file)
631b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
632014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  def GetVersionTag(self, revision):
633014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    tag = self.Git("describe --tags %s" % revision).strip()
634014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    return SanitizeVersionTag(tag)
635014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
636014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  def GetRecentReleases(self, max_age):
637014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    # Make sure tags are fetched.
638014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    self.Git("fetch origin +refs/tags/*:refs/tags/*")
639014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
640014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    # Current timestamp.
641014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    time_now = int(self._side_effect_handler.GetUTCStamp())
642014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
643014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    # List every tag from a given period.
644014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    revisions = self.Git("rev-list --max-age=%d --tags" %
645014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch                         int(time_now - max_age)).strip()
646014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
647014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    # Filter out revisions who's tag is off by one or more commits.
648014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    return filter(lambda r: self.GetVersionTag(r), revisions.splitlines())
649014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
650014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  def GetLatestVersion(self):
651014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    # Use cached version if available.
652014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    if self["latest_version"]:
653014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      return self["latest_version"]
654014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
655014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    # Make sure tags are fetched.
656014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    self.Git("fetch origin +refs/tags/*:refs/tags/*")
657014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
658014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    all_tags = self.vc.GetTags()
659014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    only_version_tags = NormalizeVersionTags(all_tags)
660014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
661014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    version = sorted(only_version_tags,
662014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch                     key=SortingKey, reverse=True)[0]
663014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    self["latest_version"] = version
664014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    return version
665014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
666014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  def GetLatestRelease(self):
667014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    """The latest release is the git hash of the latest tagged version.
668014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
669014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    This revision should be rolled into chromium.
670014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    """
671014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    latest_version = self.GetLatestVersion()
672014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
673014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    # The latest release.
674014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    latest_hash = self.GitLog(n=1, format="%H", branch=latest_version)
675014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    assert latest_hash
676014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    return latest_hash
677014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
678014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  def GetLatestReleaseBase(self, version=None):
679014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    """The latest release base is the latest revision that is covered in the
680014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    last change log file. It doesn't include cherry-picked patches.
681014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    """
682014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    latest_version = version or self.GetLatestVersion()
683014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
684014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    # Strip patch level if it exists.
685014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    latest_version = ".".join(latest_version.split(".")[:3])
686014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
687014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    # The latest release base.
688014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    latest_hash = self.GitLog(n=1, format="%H", branch=latest_version)
689014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    assert latest_hash
690014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
691014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    title = self.GitLog(n=1, format="%s", git_hash=latest_hash)
692014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    match = PUSH_MSG_GIT_RE.match(title)
693014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    if match:
694014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      # Legacy: In the old process there's one level of indirection. The
695014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      # version is on the candidates branch and points to the real release
696014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      # base on master through the commit message.
697014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      return match.group("git_rev")
698014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    match = PUSH_MSG_NEW_RE.match(title)
699014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    if match:
700014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      # This is a new-style v8 version branched from master. The commit
701014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      # "latest_hash" is the version-file change. Its parent is the release
702014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      # base on master.
703014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      return self.GitLog(n=1, format="%H", git_hash="%s^" % latest_hash)
704014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
705014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    self.Die("Unknown latest release: %s" % latest_hash)
706b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
707b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  def ArrayToVersion(self, prefix):
708b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    return ".".join([self[prefix + "major"],
709b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch                     self[prefix + "minor"],
710b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch                     self[prefix + "build"],
711b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch                     self[prefix + "patch"]])
712b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
713014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  def StoreVersion(self, version, prefix):
714014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    version_parts = version.split(".")
715014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    if len(version_parts) == 3:
716014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      version_parts.append("0")
717014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    major, minor, build, patch = version_parts
718014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    self[prefix + "major"] = major
719014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    self[prefix + "minor"] = minor
720014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    self[prefix + "build"] = build
721014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    self[prefix + "patch"] = patch
722014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
723b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  def SetVersion(self, version_file, prefix):
724b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    output = ""
725b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    for line in FileToText(version_file).splitlines():
726014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      if line.startswith("#define V8_MAJOR_VERSION"):
727b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        line = re.sub("\d+$", self[prefix + "major"], line)
728014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      elif line.startswith("#define V8_MINOR_VERSION"):
729b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        line = re.sub("\d+$", self[prefix + "minor"], line)
730014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      elif line.startswith("#define V8_BUILD_NUMBER"):
731b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        line = re.sub("\d+$", self[prefix + "build"], line)
732014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      elif line.startswith("#define V8_PATCH_LEVEL"):
733b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        line = re.sub("\d+$", self[prefix + "patch"], line)
734014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      elif (self[prefix + "candidate"] and
735014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch            line.startswith("#define V8_IS_CANDIDATE_VERSION")):
736014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch        line = re.sub("\d+$", self[prefix + "candidate"], line)
737b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      output += "%s\n" % line
738b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    TextToFile(output, version_file)
739b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
740958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier
741958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernierclass BootstrapStep(Step):
742014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  MESSAGE = "Bootstrapping checkout and state."
743958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier
744958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier  def RunStep(self):
745014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    # Reserve state entry for json output.
746014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    self['json_output'] = {}
747014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
748958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier    if os.path.realpath(self.default_cwd) == os.path.realpath(V8_BASE):
749958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier      self.Die("Can't use v8 checkout with calling script as work checkout.")
750958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier    # Directory containing the working v8 checkout.
751958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier    if not os.path.exists(self._options.work_dir):
752958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier      os.makedirs(self._options.work_dir)
753958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier    if not os.path.exists(self.default_cwd):
754958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier      self.Command("fetch", "v8", cwd=self._options.work_dir)
755b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
756b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
757b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochclass UploadStep(Step):
758b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  MESSAGE = "Upload for code review."
759b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
760b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  def RunStep(self):
761b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    if self._options.reviewer:
762b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      print "Using account %s for review." % self._options.reviewer
763b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      reviewer = self._options.reviewer
764b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    else:
765b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      print "Please enter the email address of a V8 reviewer for your patch: ",
766b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      self.DieNoManualMode("A reviewer must be specified in forced mode.")
767b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      reviewer = self.ReadLine()
768b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    self.GitUpload(reviewer, self._options.author, self._options.force_upload,
769958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier                   bypass_hooks=self._options.bypass_upload_hooks,
770958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier                   cc=self._options.cc)
771b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
772b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
773b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochdef MakeStep(step_class=Step, number=0, state=None, config=None,
774b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch             options=None, side_effect_handler=DEFAULT_SIDE_EFFECT_HANDLER):
775b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    # Allow to pass in empty dictionaries.
776b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    state = state if state is not None else {}
777b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    config = config if config is not None else {}
778b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
779b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    try:
780b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      message = step_class.MESSAGE
781b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    except AttributeError:
782b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      message = step_class.__name__
783b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
784b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    return step_class(message, number=number, config=config,
785b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch                      state=state, options=options,
786b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch                      handler=side_effect_handler)
787b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
788b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
789b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochclass ScriptsBase(object):
790b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  def __init__(self,
791b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch               config=None,
792b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch               side_effect_handler=DEFAULT_SIDE_EFFECT_HANDLER,
793b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch               state=None):
794b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    self._config = config or self._Config()
795b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    self._side_effect_handler = side_effect_handler
796b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    self._state = state if state is not None else {}
797b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
798b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  def _Description(self):
799b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    return None
800b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
801b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  def _PrepareOptions(self, parser):
802b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    pass
803b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
804b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  def _ProcessOptions(self, options):
805b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    return True
806b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
807b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  def _Steps(self):  # pragma: no cover
808b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    raise Exception("Not implemented.")
809b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
810b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  def _Config(self):
811b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    return {}
812b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
813b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  def MakeOptions(self, args=None):
814b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    parser = argparse.ArgumentParser(description=self._Description())
815b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    parser.add_argument("-a", "--author", default="",
816b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch                        help="The author email used for rietveld.")
817b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    parser.add_argument("--dry-run", default=False, action="store_true",
818b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch                        help="Perform only read-only actions.")
819014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    parser.add_argument("--json-output",
820014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch                        help="File to write results summary to.")
821b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    parser.add_argument("-r", "--reviewer", default="",
822b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch                        help="The account name to be used for reviews.")
823b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    parser.add_argument("-s", "--step",
824b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        help="Specify the step where to start work. Default: 0.",
825b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        default=0, type=int)
826958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier    parser.add_argument("--work-dir",
827958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier                        help=("Location where to bootstrap a working v8 "
828958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier                              "checkout."))
829b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    self._PrepareOptions(parser)
830b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
831b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    if args is None:  # pragma: no cover
832b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      options = parser.parse_args()
833b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    else:
834b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      options = parser.parse_args(args)
835b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
836b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    # Process common options.
837b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    if options.step < 0:  # pragma: no cover
838b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      print "Bad step number %d" % options.step
839b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      parser.print_help()
840b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      return None
841b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
842b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    # Defaults for options, common to all scripts.
843b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    options.manual = getattr(options, "manual", True)
844b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    options.force = getattr(options, "force", False)
845b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    options.bypass_upload_hooks = False
846b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
847b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    # Derived options.
848b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    options.requires_editor = not options.force
849b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    options.wait_for_lgtm = not options.force
850b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    options.force_readline_defaults = not options.manual
851b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    options.force_upload = not options.manual
852b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
853b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    # Process script specific options.
854b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    if not self._ProcessOptions(options):
855b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      parser.print_help()
856b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      return None
857958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier
858958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier    if not options.work_dir:
859958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier      options.work_dir = "/tmp/v8-release-scripts-work-dir"
860b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    return options
861b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
862b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  def RunSteps(self, step_classes, args=None):
863b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    options = self.MakeOptions(args)
864b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    if not options:
865b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      return 1
866b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
867b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    state_file = "%s-state.json" % self._config["PERSISTFILE_BASENAME"]
868b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    if options.step == 0 and os.path.exists(state_file):
869b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      os.remove(state_file)
870b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
871b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    steps = []
872958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier    for (number, step_class) in enumerate([BootstrapStep] + step_classes):
873b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      steps.append(MakeStep(step_class, number, self._state, self._config,
874b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch                            options, self._side_effect_handler))
875014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
876014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    try:
877014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      for step in steps[options.step:]:
878014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch        if step.Run():
879014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch          return 0
880014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    finally:
881014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      if options.json_output:
882014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch        with open(options.json_output, "w") as f:
883014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch          json.dump(self._state['json_output'], f)
884014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
885b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    return 0
886b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
887b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  def Run(self, args=None):
888b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    return self.RunSteps(self._Steps(), args)
889