1b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch#!/usr/bin/env python
2b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch# Copyright 2014 the V8 project authors. All rights reserved.
3b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch# Use of this source code is governed by a BSD-style license that can be
4b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch# found in the LICENSE file.
5b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
6014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch# This script retrieves the history of all V8 branches and
7b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch# their corresponding Chromium revisions.
8b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
9b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch# Requires a chromium checkout with branch heads:
10b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch# gclient sync --with_branch_heads
11b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch# gclient fetch
12b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
13b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochimport argparse
14b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochimport csv
15b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochimport itertools
16b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochimport json
17b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochimport os
18b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochimport re
19b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochimport sys
20b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
21b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochfrom common_includes import *
22b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
23b8a8cc1952d61a2f3a2568848933943a543b5d3eBen MurdochCONFIG = {
24b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  "BRANCHNAME": "retrieve-v8-releases",
25b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  "PERSISTFILE_BASENAME": "/tmp/v8-releases-tempfile",
26b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch}
27b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
28b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch# Expression for retrieving the bleeding edge revision from a commit message.
29958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily BernierPUSH_MSG_SVN_RE = re.compile(r".* \(based on bleeding_edge revision r(\d+)\)$")
30958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily BernierPUSH_MSG_GIT_RE = re.compile(r".* \(based on ([a-fA-F0-9]+)\)$")
31b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
32b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch# Expression for retrieving the merged patches from a merge commit message
33b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch# (old and new format).
34b8a8cc1952d61a2f3a2568848933943a543b5d3eBen MurdochMERGE_MESSAGE_RE = re.compile(r"^.*[M|m]erged (.+)(\)| into).*$", re.M)
35b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
36958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily BernierCHERRY_PICK_TITLE_GIT_RE = re.compile(r"^.* \(cherry\-pick\)\.?$")
37958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier
38958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier# New git message for cherry-picked CLs. One message per line.
39958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily BernierMERGE_MESSAGE_GIT_RE = re.compile(r"^Merged ([a-fA-F0-9]+)\.?$")
40958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier
41b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch# Expression for retrieving reverted patches from a commit message (old and
42b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch# new format).
43b8a8cc1952d61a2f3a2568848933943a543b5d3eBen MurdochROLLBACK_MESSAGE_RE = re.compile(r"^.*[R|r]ollback of (.+)(\)| in).*$", re.M)
44b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
45958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier# New git message for reverted CLs. One message per line.
46958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily BernierROLLBACK_MESSAGE_GIT_RE = re.compile(r"^Rollback of ([a-fA-F0-9]+)\.?$")
47958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier
48b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch# Expression for retrieving the code review link.
49b8a8cc1952d61a2f3a2568848933943a543b5d3eBen MurdochREVIEW_LINK_RE = re.compile(r"^Review URL: (.+)$", re.M)
50b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
51b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch# Expression with three versions (historical) for extracting the v8 revision
52b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch# from the chromium DEPS file.
53b8a8cc1952d61a2f3a2568848933943a543b5d3eBen MurdochDEPS_RE = re.compile(r"""^\s*(?:["']v8_revision["']: ["']"""
54b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch                     """|\(Var\("googlecode_url"\) % "v8"\) \+ "\/trunk@"""
55b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch                     """|"http\:\/\/v8\.googlecode\.com\/svn\/trunk@)"""
56b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch                     """([^"']+)["'].*$""", re.M)
57b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
58b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch# Expression to pick tag and revision for bleeding edge tags. To be used with
59b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch# output of 'svn log'.
60b8a8cc1952d61a2f3a2568848933943a543b5d3eBen MurdochBLEEDING_EDGE_TAGS_RE = re.compile(
61b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    r"A \/tags\/([^\s]+) \(from \/branches\/bleeding_edge\:(\d+)\)")
62b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
63014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben MurdochOMAHA_PROXY_URL = "http://omahaproxy.appspot.com/"
64b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
65b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochdef SortBranches(branches):
66b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  """Sort branches with version number names."""
67b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  return sorted(branches, key=SortingKey, reverse=True)
68b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
69b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
70b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochdef FilterDuplicatesAndReverse(cr_releases):
71b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  """Returns the chromium releases in reverse order filtered by v8 revision
72b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  duplicates.
73b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
74014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  cr_releases is a list of [cr_rev, v8_hsh] reverse-sorted by cr_rev.
75b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  """
76b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  last = ""
77b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  result = []
78b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  for release in reversed(cr_releases):
79b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    if last == release[1]:
80b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      continue
81b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    last = release[1]
82b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    result.append(release)
83b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  return result
84b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
85b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
86b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochdef BuildRevisionRanges(cr_releases):
87b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  """Returns a mapping of v8 revision -> chromium ranges.
88b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  The ranges are comma-separated, each range has the form R1:R2. The newest
89b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  entry is the only one of the form R1, as there is no end range.
90b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
91014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  cr_releases is a list of [cr_rev, v8_hsh] reverse-sorted by cr_rev.
92014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  cr_rev either refers to a chromium commit position or a chromium branch
93014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  number.
94b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  """
95b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  range_lists = {}
96b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  cr_releases = FilterDuplicatesAndReverse(cr_releases)
97b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
98b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  # Visit pairs of cr releases from oldest to newest.
99b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  for cr_from, cr_to in itertools.izip(
100b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      cr_releases, itertools.islice(cr_releases, 1, None)):
101b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
102b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    # Assume the chromium revisions are all different.
103b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    assert cr_from[0] != cr_to[0]
104b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
105b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    ran = "%s:%d" % (cr_from[0], int(cr_to[0]) - 1)
106b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
107b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    # Collect the ranges in lists per revision.
108b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    range_lists.setdefault(cr_from[1], []).append(ran)
109b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
110b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  # Add the newest revision.
111b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  if cr_releases:
112b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    range_lists.setdefault(cr_releases[-1][1], []).append(cr_releases[-1][0])
113b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
114b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  # Stringify and comma-separate the range lists.
115014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  return dict((hsh, ", ".join(ran)) for hsh, ran in range_lists.iteritems())
116b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
117b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
118b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochdef MatchSafe(match):
119b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  if match:
120b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    return match.group(1)
121b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  else:
122b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    return ""
123b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
124b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
125b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochclass Preparation(Step):
126b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  MESSAGE = "Preparation."
127b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
128b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  def RunStep(self):
129b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    self.CommonPrepare()
130b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    self.PrepareBranch()
131b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
132b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
133b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochclass RetrieveV8Releases(Step):
134b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  MESSAGE = "Retrieve all V8 releases."
135b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
136b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  def ExceedsMax(self, releases):
137b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    return (self._options.max_releases > 0
138b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch            and len(releases) > self._options.max_releases)
139b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
140014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  def GetMasterHashFromPush(self, title):
141958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier    return MatchSafe(PUSH_MSG_GIT_RE.match(title))
142b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
143b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  def GetMergedPatches(self, body):
144b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    patches = MatchSafe(MERGE_MESSAGE_RE.search(body))
145b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    if not patches:
146b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      patches = MatchSafe(ROLLBACK_MESSAGE_RE.search(body))
147b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      if patches:
148b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        # Indicate reverted patches with a "-".
149b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        patches = "-%s" % patches
150b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    return patches
151b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
152958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier  def GetMergedPatchesGit(self, body):
153958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier    patches = []
154958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier    for line in body.splitlines():
155958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier      patch = MatchSafe(MERGE_MESSAGE_GIT_RE.match(line))
156958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier      if patch:
157958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier        patches.append(patch)
158958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier      patch = MatchSafe(ROLLBACK_MESSAGE_GIT_RE.match(line))
159958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier      if patch:
160958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier        patches.append("-%s" % patch)
161958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier    return ", ".join(patches)
162958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier
163958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier
164b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  def GetReleaseDict(
165014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      self, git_hash, master_position, master_hash, branch, version,
166958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier      patches, cl_body):
167958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier    revision = self.GetCommitPositionNumber(git_hash)
168b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    return {
169958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier      # The cr commit position number on the branch.
170b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      "revision": revision,
171958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier      # The git revision on the branch.
172958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier      "revision_git": git_hash,
173958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier      # The cr commit position number on master.
174014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      "master_position": master_position,
175958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier      # The same for git.
176014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      "master_hash": master_hash,
177b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      # The branch name.
178b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      "branch": branch,
179b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      # The version for displaying in the form 3.26.3 or 3.26.3.12.
180b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      "version": version,
181b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      # The date of the commit.
182b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      "date": self.GitLog(n=1, format="%ci", git_hash=git_hash),
183b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      # Merged patches if available in the form 'r1234, r2345'.
184b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      "patches_merged": patches,
185b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      # Default for easier output formatting.
186b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      "chromium_revision": "",
187b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      # Default for easier output formatting.
188b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      "chromium_branch": "",
189014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      # Link to the CL on code review. Candiates pushes are not uploaded,
190014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      # so this field will be populated below with the recent roll CL link.
191b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      "review_link": MatchSafe(REVIEW_LINK_RE.search(cl_body)),
192b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      # Link to the commit message on google code.
193b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      "revision_link": ("https://code.google.com/p/v8/source/detail?r=%s"
194b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch                        % revision),
195b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    }
196b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
197b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  def GetRelease(self, git_hash, branch):
198b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    self.ReadAndPersistVersion()
199b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    base_version = [self["major"], self["minor"], self["build"]]
200b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    version = ".".join(base_version)
201b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    body = self.GitLog(n=1, format="%B", git_hash=git_hash)
202b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
203b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    patches = ""
204b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    if self["patch"] != "0":
205b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      version += ".%s" % self["patch"]
206958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier      if CHERRY_PICK_TITLE_GIT_RE.match(body.splitlines()[0]):
207958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier        patches = self.GetMergedPatchesGit(body)
208958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier      else:
209958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier        patches = self.GetMergedPatches(body)
210b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
211014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    if SortingKey("4.2.69") <= SortingKey(version):
212014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      master_hash = self.GetLatestReleaseBase(version=version)
213014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    else:
214014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      # Legacy: Before version 4.2.69, the master revision was determined
215014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      # by commit message.
216014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      title = self.GitLog(n=1, format="%s", git_hash=git_hash)
217014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      master_hash = self.GetMasterHashFromPush(title)
218014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    master_position = ""
219014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    if master_hash:
220014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      master_position = self.GetCommitPositionNumber(master_hash)
221b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    return self.GetReleaseDict(
222014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch        git_hash, master_position, master_hash, branch, version,
223b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        patches, body), self["patch"]
224b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
225b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  def GetReleasesFromBranch(self, branch):
226958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier    self.GitReset(self.vc.RemoteBranch(branch))
227958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier    if branch == self.vc.MasterBranch():
228958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier      return self.GetReleasesFromMaster()
229b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
230b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    releases = []
231b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    try:
232b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      for git_hash in self.GitLog(format="%H").splitlines():
233b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        if VERSION_FILE not in self.GitChangedFiles(git_hash):
234b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch          continue
235b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        if self.ExceedsMax(releases):
236b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch          break  # pragma: no cover
237b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        if not self.GitCheckoutFileSafe(VERSION_FILE, git_hash):
238b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch          break  # pragma: no cover
239b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
240b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        release, patch_level = self.GetRelease(git_hash, branch)
241b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        releases.append(release)
242b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
243b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        # Follow branches only until their creation point.
244b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        # TODO(machenbach): This omits patches if the version file wasn't
245b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        # manipulated correctly. Find a better way to detect the point where
246b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        # the parent of the branch head leads to the trunk branch.
247958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier        if branch != self.vc.CandidateBranch() and patch_level == "0":
248b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch          break
249b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
250b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    # Allow Ctrl-C interrupt.
251b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    except (KeyboardInterrupt, SystemExit):  # pragma: no cover
252b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      pass
253b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
254b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    # Clean up checked-out version file.
255b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    self.GitCheckoutFileSafe(VERSION_FILE, "HEAD")
256b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    return releases
257b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
258014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  def GetReleaseFromRevision(self, revision):
259014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    releases = []
260014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    try:
261014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      if (VERSION_FILE not in self.GitChangedFiles(revision) or
262014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch          not self.GitCheckoutFileSafe(VERSION_FILE, revision)):
263014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch        print "Skipping revision %s" % revision
264014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch        return []  # pragma: no cover
265014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
266014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      branches = map(
267014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch          str.strip,
268014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch          self.Git("branch -r --contains %s" % revision).strip().splitlines(),
269014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      )
270014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      branch = ""
271014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      for b in branches:
272014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch        if b.startswith("origin/"):
273014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch          branch = b.split("origin/")[1]
274014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch          break
275014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch        if b.startswith("branch-heads/"):
276014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch          branch = b.split("branch-heads/")[1]
277014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch          break
278014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      else:
279014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch        print "Could not determine branch for %s" % revision
280014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
281014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      release, _ = self.GetRelease(revision, branch)
282014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      releases.append(release)
283014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
284014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    # Allow Ctrl-C interrupt.
285014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    except (KeyboardInterrupt, SystemExit):  # pragma: no cover
286014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      pass
287014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
288014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    # Clean up checked-out version file.
289014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    self.GitCheckoutFileSafe(VERSION_FILE, "HEAD")
290014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    return releases
291014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
292014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
293b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  def RunStep(self):
294b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    self.GitCreateBranch(self._config["BRANCHNAME"])
295b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    releases = []
296b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    if self._options.branch == 'recent':
297014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      # List every release from the last 7 days.
298014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      revisions = self.GetRecentReleases(max_age=7 * DAY_IN_SECONDS)
299014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      for revision in revisions:
300014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch        releases += self.GetReleaseFromRevision(revision)
301b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    elif self._options.branch == 'all':  # pragma: no cover
302b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      # Retrieve the full release history.
303014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      for branch in self.vc.GetBranches():
304b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        releases += self.GetReleasesFromBranch(branch)
305958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier      releases += self.GetReleasesFromBranch(self.vc.CandidateBranch())
306958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier      releases += self.GetReleasesFromBranch(self.vc.MasterBranch())
307b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    else:  # pragma: no cover
308b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      # Retrieve history for a specified branch.
309014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      assert self._options.branch in (self.vc.GetBranches() +
310958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier          [self.vc.CandidateBranch(), self.vc.MasterBranch()])
311b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      releases += self.GetReleasesFromBranch(self._options.branch)
312b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
313b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    self["releases"] = sorted(releases,
314b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch                              key=lambda r: SortingKey(r["version"]),
315b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch                              reverse=True)
316b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
317b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
318b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochclass UpdateChromiumCheckout(Step):
319014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  MESSAGE = "Update the chromium checkout."
320b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
321b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  def RunStep(self):
322b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    cwd = self._options.chromium
323014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    self.GitFetchOrigin("+refs/heads/*:refs/remotes/origin/*",
324014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch                        "+refs/branch-heads/*:refs/remotes/branch-heads/*",
325014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch                        cwd=cwd)
326014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    # Update v8 checkout in chromium.
327014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    self.GitFetchOrigin(cwd=os.path.join(cwd, "v8"))
328b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
329b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
330b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochdef ConvertToCommitNumber(step, revision):
331b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  # Simple check for git hashes.
332b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  if revision.isdigit() and len(revision) < 8:
333b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    return revision
334958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier  return step.GetCommitPositionNumber(
335b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      revision, cwd=os.path.join(step._options.chromium, "v8"))
336b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
337b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
338b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochclass RetrieveChromiumV8Releases(Step):
339b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  MESSAGE = "Retrieve V8 releases from Chromium DEPS."
340b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
341b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  def RunStep(self):
342b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    cwd = self._options.chromium
343b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
344014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    # All v8 revisions we are interested in.
345014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    releases_dict = dict((r["revision_git"], r) for r in self["releases"])
346b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
347b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    cr_releases = []
348014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    count_past_last_v8 = 0
349b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    try:
350b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      for git_hash in self.GitLog(
351014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch          format="%H", grep="V8", branch="origin/master",
352014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch          path="DEPS", cwd=cwd).splitlines():
353014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch        deps = self.GitShowFile(git_hash, "DEPS", cwd=cwd)
354b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        match = DEPS_RE.search(deps)
355b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        if match:
356b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch          cr_rev = self.GetCommitPositionNumber(git_hash, cwd=cwd)
357b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch          if cr_rev:
358014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch            v8_hsh = match.group(1)
359014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch            cr_releases.append([cr_rev, v8_hsh])
360014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
361014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch          if count_past_last_v8:
362014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch            count_past_last_v8 += 1  # pragma: no cover
363b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
364014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch          if count_past_last_v8 > 20:
365b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch            break  # pragma: no cover
366b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
367014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch          # Stop as soon as we find a v8 revision that we didn't fetch in the
368014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch          # v8-revision-retrieval part above (i.e. a revision that's too old).
369014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch          # Just iterate a few more times in case there were reverts.
370014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch          if v8_hsh not in releases_dict:
371014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch            count_past_last_v8 += 1  # pragma: no cover
372014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
373b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    # Allow Ctrl-C interrupt.
374b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    except (KeyboardInterrupt, SystemExit):  # pragma: no cover
375b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      pass
376b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
377014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    # Add the chromium ranges to the v8 candidates and master releases.
378b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    all_ranges = BuildRevisionRanges(cr_releases)
379014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
380014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    for hsh, ranges in all_ranges.iteritems():
381014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      releases_dict.get(hsh, {})["chromium_revision"] = ranges
382b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
383b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
384b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch# TODO(machenbach): Unify common code with method above.
385014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdochclass RetrieveChromiumBranches(Step):
386b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  MESSAGE = "Retrieve Chromium branch information."
387b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
388b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  def RunStep(self):
389b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    cwd = self._options.chromium
390b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
391014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    # All v8 revisions we are interested in.
392014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    releases_dict = dict((r["revision_git"], r) for r in self["releases"])
393b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
394b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    # Filter out irrelevant branches.
395b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    branches = filter(lambda r: re.match(r"branch-heads/\d+", r),
396b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch                      self.GitRemotes(cwd=cwd))
397b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
398b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    # Transform into pure branch numbers.
399b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    branches = map(lambda r: int(re.match(r"branch-heads/(\d+)", r).group(1)),
400b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch                   branches)
401b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
402b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    branches = sorted(branches, reverse=True)
403b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
404b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    cr_branches = []
405014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    count_past_last_v8 = 0
406b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    try:
407b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      for branch in branches:
408014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch        deps = self.GitShowFile(
409014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch            "refs/branch-heads/%d" % branch, "DEPS", cwd=cwd)
410b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        match = DEPS_RE.search(deps)
411b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        if match:
412014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch          v8_hsh = match.group(1)
413014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch          cr_branches.append([str(branch), v8_hsh])
414014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
415014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch          if count_past_last_v8:
416014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch            count_past_last_v8 += 1  # pragma: no cover
417b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
418014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch          if count_past_last_v8 > 20:
419b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch            break  # pragma: no cover
420b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
421014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch          # Stop as soon as we find a v8 revision that we didn't fetch in the
422014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch          # v8-revision-retrieval part above (i.e. a revision that's too old).
423014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch          # Just iterate a few more times in case there were reverts.
424014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch          if v8_hsh not in releases_dict:
425014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch            count_past_last_v8 += 1  # pragma: no cover
426014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
427b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    # Allow Ctrl-C interrupt.
428b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    except (KeyboardInterrupt, SystemExit):  # pragma: no cover
429b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      pass
430b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
431014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    # Add the chromium branches to the v8 candidate releases.
432b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    all_ranges = BuildRevisionRanges(cr_branches)
433b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    for revision, ranges in all_ranges.iteritems():
434014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      releases_dict.get(revision, {})["chromium_branch"] = ranges
435014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
436014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
437014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdochclass RetrieveInformationOnChromeReleases(Step):
438014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  MESSAGE = 'Retrieves relevant information on the latest Chrome releases'
439014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
440014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  def Run(self):
441014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
442014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    params = None
443014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    result_raw = self.ReadURL(
444014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch                             OMAHA_PROXY_URL + "all.json",
445014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch                             params,
446014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch                             wait_plan=[5, 20]
447014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch                             )
448014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    recent_releases = json.loads(result_raw)
449014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
450014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    canaries = []
451014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
452014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    for current_os in recent_releases:
453014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      for current_version in current_os["versions"]:
454014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch        if current_version["channel"] != "canary":
455014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch          continue
456014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
457014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch        current_candidate = self._CreateCandidate(current_version)
458014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch        canaries.append(current_candidate)
459014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
460014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    chrome_releases = {"canaries": canaries}
461014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    self["chrome_releases"] = chrome_releases
462014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
463014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  def _GetGitHashForV8Version(self, v8_version):
464014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    if v8_version == "N/A":
465014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      return ""
466014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
467014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    real_v8_version = v8_version
468014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    if v8_version.split(".")[3]== "0":
469014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      real_v8_version = v8_version[:-2]
470014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
471014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    try:
472014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      return self.GitGetHashOfTag(real_v8_version)
473014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    except GitFailedException:
474014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      return ""
475014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
476014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  def _CreateCandidate(self, current_version):
477014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    params = None
478014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    url_to_call = (OMAHA_PROXY_URL + "v8.json?version="
479014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch                   + current_version["previous_version"])
480014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    result_raw = self.ReadURL(
481014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch                         url_to_call,
482014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch                         params,
483014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch                         wait_plan=[5, 20]
484014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch                         )
485014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    previous_v8_version = json.loads(result_raw)["v8_version"]
486014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    v8_previous_version_hash = self._GetGitHashForV8Version(previous_v8_version)
487014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
488014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    current_v8_version = current_version["v8_version"]
489014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    v8_version_hash = self._GetGitHashForV8Version(current_v8_version)
490014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
491014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    current_candidate = {
492014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch                        "chrome_version": current_version["version"],
493014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch                        "os": current_version["os"],
494014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch                        "release_date": current_version["current_reldate"],
495014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch                        "v8_version": current_v8_version,
496014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch                        "v8_version_hash": v8_version_hash,
497014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch                        "v8_previous_version": previous_v8_version,
498014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch                        "v8_previous_version_hash": v8_previous_version_hash,
499014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch                       }
500014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    return current_candidate
501b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
502b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
503b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochclass CleanUp(Step):
504b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  MESSAGE = "Clean up."
505b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
506b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  def RunStep(self):
507b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    self.CommonCleanup()
508b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
509b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
510b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochclass WriteOutput(Step):
511b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  MESSAGE = "Print output."
512b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
513b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  def Run(self):
514014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
515014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    output = {
516014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch              "releases": self["releases"],
517014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch              "chrome_releases": self["chrome_releases"],
518014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch              }
519014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
520b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    if self._options.csv:
521b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      with open(self._options.csv, "w") as f:
522b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        writer = csv.DictWriter(f,
523b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch                                ["version", "branch", "revision",
524b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch                                 "chromium_revision", "patches_merged"],
525b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch                                restval="",
526b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch                                extrasaction="ignore")
527b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        for release in self["releases"]:
528b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch          writer.writerow(release)
529b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    if self._options.json:
530b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      with open(self._options.json, "w") as f:
531014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch        f.write(json.dumps(output))
532b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    if not self._options.csv and not self._options.json:
533014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      print output  # pragma: no cover
534b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
535b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
536b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochclass Releases(ScriptsBase):
537b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  def _PrepareOptions(self, parser):
538b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    parser.add_argument("-b", "--branch", default="recent",
539b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch                        help=("The branch to analyze. If 'all' is specified, "
540b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch                              "analyze all branches. If 'recent' (default) "
541014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch                              "is specified, track beta, stable and "
542014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch                              "candidates."))
543b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    parser.add_argument("-c", "--chromium",
544b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch                        help=("The path to your Chromium src/ "
545b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch                              "directory to automate the V8 roll."))
546b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    parser.add_argument("--csv", help="Path to a CSV file for export.")
547b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    parser.add_argument("-m", "--max-releases", type=int, default=0,
548b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch                        help="The maximum number of releases to track.")
549b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    parser.add_argument("--json", help="Path to a JSON file for export.")
550b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
551b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  def _ProcessOptions(self, options):  # pragma: no cover
552014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    options.force_readline_defaults = True
553b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    return True
554b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
555b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  def _Config(self):
556b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    return {
557b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      "BRANCHNAME": "retrieve-v8-releases",
558b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      "PERSISTFILE_BASENAME": "/tmp/v8-releases-tempfile",
559b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    }
560b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
561b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  def _Steps(self):
562014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
563b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    return [
564b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      Preparation,
565b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      RetrieveV8Releases,
566b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      UpdateChromiumCheckout,
567b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      RetrieveChromiumV8Releases,
568014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      RetrieveChromiumBranches,
569014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      RetrieveInformationOnChromeReleases,
570b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      CleanUp,
571b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      WriteOutput,
572b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    ]
573b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
574b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
575b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochif __name__ == "__main__":  # pragma: no cover
576b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  sys.exit(Releases().Run())
577