1474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org#!/usr/bin/env python
2474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org# Copyright 2014 the V8 project authors. All rights reserved.
3474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org# Use of this source code is governed by a BSD-style license that can be
4474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org# found in the LICENSE file.
5474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org
6474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.orgimport argparse
7474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.orgimport sys
8474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org
9474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.orgfrom common_includes import *
10474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org
11474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org
12474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.orgclass Preparation(Step):
13474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org  MESSAGE = "Preparation."
14474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org
15474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org  def RunStep(self):
16474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org    self.CommonPrepare()
17474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org    self.PrepareBranch()
18474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org    self.GitCheckout("master")
19474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org    self.GitSVNRebase()
20474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org
21474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org
22474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.orgclass GetTags(Step):
23474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org  MESSAGE = "Get all V8 tags."
24474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org
25474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org  def RunStep(self):
2606b2696801712948b665372a38f96b1f10be6997machenbach@chromium.org    self.GitCreateBranch(self._config["BRANCHNAME"])
27474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org
28474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org    # Get remote tags.
29474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org    tags = filter(lambda s: re.match(r"^svn/tags/[\d+\.]+$", s),
30474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org                  self.GitRemotes())
31474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org
32474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org    # Remove 'svn/tags/' prefix.
33474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org    self["tags"] = map(lambda s: s[9:], tags)
34474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org
35474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org
36474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.orgclass GetOldestUntaggedVersion(Step):
37474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org  MESSAGE = "Check if there's a version on bleeding edge without a tag."
38474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org
39474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org  def RunStep(self):
40474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org    tags = set(self["tags"])
41474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org    self["candidate"] = None
42474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org    self["candidate_version"] = None
43474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org    self["next"] = None
44474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org    self["next_version"] = None
45474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org
46474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org    # Iterate backwards through all automatic version updates.
47474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org    for git_hash in self.GitLog(
48474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org        format="%H", grep="\\[Auto\\-roll\\] Bump up version to").splitlines():
49474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org
50474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org      # Get the version.
5106b2696801712948b665372a38f96b1f10be6997machenbach@chromium.org      if not self.GitCheckoutFileSafe(VERSION_FILE, git_hash):
52474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org        continue
53474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org
54474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org      self.ReadAndPersistVersion()
55474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org      version = self.ArrayToVersion("")
56474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org
57474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org      # Strip off trailing patch level (tags don't include tag level 0).
58474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org      if version.endswith(".0"):
59474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org        version = version[:-2]
60474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org
61474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org      # Clean up checked-out version file.
6206b2696801712948b665372a38f96b1f10be6997machenbach@chromium.org      self.GitCheckoutFileSafe(VERSION_FILE, "HEAD")
63474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org
64474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org      if version in tags:
65474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org        if self["candidate"]:
66474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org          # Revision "git_hash" is tagged already and "candidate" was the next
67474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org          # newer revision without a tag.
68474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org          break
69474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org        else:
70474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org          print("Stop as %s is the latest version and it has been tagged." %
71474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org                version)
72474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org          self.CommonCleanup()
73474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org          return True
74474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org      else:
75474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org        # This is the second oldest version without a tag.
76474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org        self["next"] = self["candidate"]
77474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org        self["next_version"] = self["candidate_version"]
78474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org
79474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org        # This is the oldest version without a tag.
80474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org        self["candidate"] = git_hash
81474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org        self["candidate_version"] = version
82474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org
83474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org    if not self["candidate"] or not self["candidate_version"]:
84474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org      print "Nothing found to tag."
85474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org      self.CommonCleanup()
86474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org      return True
87474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org
88474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org    print("Candidate for tagging is %s with version %s" %
89474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org          (self["candidate"], self["candidate_version"]))
90474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org
91474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org
92474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.orgclass GetLKGRs(Step):
93474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org  MESSAGE = "Get the last lkgrs."
94474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org
95474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org  def RunStep(self):
96474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org    revision_url = "https://v8-status.appspot.com/revisions?format=json"
97474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org    status_json = self.ReadURL(revision_url, wait_plan=[5, 20])
98474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org    self["lkgrs"] = [entry["revision"]
99474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org                     for entry in json.loads(status_json) if entry["status"]]
100474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org
101474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org
102474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.orgclass CalculateTagRevision(Step):
103474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org  MESSAGE = "Calculate the revision to tag."
104474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org
105474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org  def LastLKGR(self, min_rev, max_rev):
106474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org    """Finds the newest lkgr between min_rev (inclusive) and max_rev
107474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org    (exclusive).
108474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org    """
109474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org    for lkgr in self["lkgrs"]:
110474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org      # LKGRs are reverse sorted.
111474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org      if int(min_rev) <= int(lkgr) and int(lkgr) < int(max_rev):
112474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org        return lkgr
113f2af15a6b44ea6276bdd609ee122babe52842a42machenbach@chromium.org    return None
114474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org
115474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org  def RunStep(self):
116474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org    # Get the lkgr after the tag candidate and before the next tag candidate.
117474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org    candidate_svn = self.GitSVNFindSVNRev(self["candidate"])
118474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org    if self["next"]:
119474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org      next_svn = self.GitSVNFindSVNRev(self["next"])
120474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org    else:
121474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org      # Don't include the version change commit itself if there is no upper
122474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org      # limit yet.
123474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org      candidate_svn =  str(int(candidate_svn) + 1)
124474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org      next_svn = sys.maxint
125474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org    lkgr_svn = self.LastLKGR(candidate_svn, next_svn)
126474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org
127474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org    if not lkgr_svn:
128474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org      print "There is no lkgr since the candidate version yet."
129474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org      self.CommonCleanup()
130474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org      return True
131474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org
132474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org    # Let's check if the lkgr is at least three hours old.
133474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org    self["lkgr"] = self.GitSVNFindGitHash(lkgr_svn)
134474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org    if not self["lkgr"]:
135474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org      print "Couldn't find git hash for lkgr %s" % lkgr_svn
136474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org      self.CommonCleanup()
137474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org      return True
138474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org
139474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org    lkgr_utc_time = int(self.GitLog(n=1, format="%at", git_hash=self["lkgr"]))
140474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org    current_utc_time = self._side_effect_handler.GetUTCStamp()
141474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org
142474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org    if current_utc_time < lkgr_utc_time + 10800:
143474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org      print "Candidate lkgr %s is too recent for tagging." % lkgr_svn
144474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org      self.CommonCleanup()
145474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org      return True
146474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org
147474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org    print "Tagging revision %s with %s" % (lkgr_svn, self["candidate_version"])
148474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org
149474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org
150474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.orgclass MakeTag(Step):
151474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org  MESSAGE = "Tag the version."
152474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org
153474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org  def RunStep(self):
154474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org    if not self._options.dry_run:
155474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org      self.GitReset(self["lkgr"])
156474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org      self.GitSVNTag(self["candidate_version"])
157474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org
158474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org
159474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.orgclass CleanUp(Step):
160474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org  MESSAGE = "Clean up."
161474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org
162474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org  def RunStep(self):
163474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org    self.CommonCleanup()
164474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org
165474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org
166474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.orgclass AutoTag(ScriptsBase):
167474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org  def _PrepareOptions(self, parser):
168474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org    parser.add_argument("--dry_run", help="Don't tag the new version.",
169474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org                        default=False, action="store_true")
170474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org
171474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org  def _ProcessOptions(self, options):  # pragma: no cover
172474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org    if not options.dry_run and not options.author:
173474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org      print "Specify your chromium.org email with -a"
174474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org      return False
175474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org    options.wait_for_lgtm = False
176474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org    options.force_readline_defaults = True
177474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org    options.force_upload = True
178474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org    return True
179474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org
18006b2696801712948b665372a38f96b1f10be6997machenbach@chromium.org  def _Config(self):
18106b2696801712948b665372a38f96b1f10be6997machenbach@chromium.org    return {
18206b2696801712948b665372a38f96b1f10be6997machenbach@chromium.org      "BRANCHNAME": "auto-tag-v8",
18306b2696801712948b665372a38f96b1f10be6997machenbach@chromium.org      "PERSISTFILE_BASENAME": "/tmp/v8-auto-tag-tempfile",
18406b2696801712948b665372a38f96b1f10be6997machenbach@chromium.org    }
18506b2696801712948b665372a38f96b1f10be6997machenbach@chromium.org
186474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org  def _Steps(self):
187474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org    return [
188474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org      Preparation,
189474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org      GetTags,
190474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org      GetOldestUntaggedVersion,
191474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org      GetLKGRs,
192474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org      CalculateTagRevision,
193474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org      MakeTag,
194474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org      CleanUp,
195474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org    ]
196474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org
197474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org
198474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.orgif __name__ == "__main__":  # pragma: no cover
19906b2696801712948b665372a38f96b1f10be6997machenbach@chromium.org  sys.exit(AutoTag().Run())
200