Home | History | Annotate | Download | only in release
      1 #!/usr/bin/env python
      2 # Copyright 2014 the V8 project authors. All rights reserved.
      3 # Use of this source code is governed by a BSD-style license that can be
      4 # found in the LICENSE file.
      5 
      6 import argparse
      7 import sys
      8 
      9 from common_includes import *
     10 
     11 
     12 class Preparation(Step):
     13   MESSAGE = "Preparation."
     14 
     15   def RunStep(self):
     16     # TODO(machenbach): Remove after the git switch.
     17     if self.Config("PERSISTFILE_BASENAME") == "/tmp/v8-auto-tag-tempfile":
     18       print "This script is disabled until after the v8 git migration."
     19       return True
     20 
     21     self.CommonPrepare()
     22     self.PrepareBranch()
     23     self.GitCheckout("master")
     24     self.vc.Pull()
     25 
     26 
     27 class GetTags(Step):
     28   MESSAGE = "Get all V8 tags."
     29 
     30   def RunStep(self):
     31     self.GitCreateBranch(self._config["BRANCHNAME"])
     32     self["tags"] = self.vc.GetTags()
     33 
     34 
     35 class GetOldestUntaggedVersion(Step):
     36   MESSAGE = "Check if there's a version on bleeding edge without a tag."
     37 
     38   def RunStep(self):
     39     tags = set(self["tags"])
     40     self["candidate"] = None
     41     self["candidate_version"] = None
     42     self["next"] = None
     43     self["next_version"] = None
     44 
     45     # Iterate backwards through all automatic version updates.
     46     for git_hash in self.GitLog(
     47         format="%H", grep="\\[Auto\\-roll\\] Bump up version to").splitlines():
     48 
     49       # Get the version.
     50       if not self.GitCheckoutFileSafe(VERSION_FILE, git_hash):
     51         continue
     52 
     53       self.ReadAndPersistVersion()
     54       version = self.ArrayToVersion("")
     55 
     56       # Strip off trailing patch level (tags don't include tag level 0).
     57       if version.endswith(".0"):
     58         version = version[:-2]
     59 
     60       # Clean up checked-out version file.
     61       self.GitCheckoutFileSafe(VERSION_FILE, "HEAD")
     62 
     63       if version in tags:
     64         if self["candidate"]:
     65           # Revision "git_hash" is tagged already and "candidate" was the next
     66           # newer revision without a tag.
     67           break
     68         else:
     69           print("Stop as %s is the latest version and it has been tagged." %
     70                 version)
     71           self.CommonCleanup()
     72           return True
     73       else:
     74         # This is the second oldest version without a tag.
     75         self["next"] = self["candidate"]
     76         self["next_version"] = self["candidate_version"]
     77 
     78         # This is the oldest version without a tag.
     79         self["candidate"] = git_hash
     80         self["candidate_version"] = version
     81 
     82     if not self["candidate"] or not self["candidate_version"]:
     83       print "Nothing found to tag."
     84       self.CommonCleanup()
     85       return True
     86 
     87     print("Candidate for tagging is %s with version %s" %
     88           (self["candidate"], self["candidate_version"]))
     89 
     90 
     91 class GetLKGRs(Step):
     92   MESSAGE = "Get the last lkgrs."
     93 
     94   def RunStep(self):
     95     revision_url = "https://v8-status.appspot.com/revisions?format=json"
     96     status_json = self.ReadURL(revision_url, wait_plan=[5, 20])
     97     self["lkgrs"] = [entry["revision"]
     98                      for entry in json.loads(status_json) if entry["status"]]
     99 
    100 
    101 class CalculateTagRevision(Step):
    102   MESSAGE = "Calculate the revision to tag."
    103 
    104   def LastLKGR(self, min_rev, max_rev):
    105     """Finds the newest lkgr between min_rev (inclusive) and max_rev
    106     (exclusive).
    107     """
    108     for lkgr in self["lkgrs"]:
    109       # LKGRs are reverse sorted.
    110       if int(min_rev) <= int(lkgr) and int(lkgr) < int(max_rev):
    111         return lkgr
    112     return None
    113 
    114   def RunStep(self):
    115     # Get the lkgr after the tag candidate and before the next tag candidate.
    116     candidate_svn = self.vc.GitSvn(self["candidate"])
    117     if self["next"]:
    118       next_svn = self.vc.GitSvn(self["next"])
    119     else:
    120       # Don't include the version change commit itself if there is no upper
    121       # limit yet.
    122       candidate_svn =  str(int(candidate_svn) + 1)
    123       next_svn = sys.maxint
    124     lkgr_svn = self.LastLKGR(candidate_svn, next_svn)
    125 
    126     if not lkgr_svn:
    127       print "There is no lkgr since the candidate version yet."
    128       self.CommonCleanup()
    129       return True
    130 
    131     # Let's check if the lkgr is at least three hours old.
    132     self["lkgr"] = self.vc.SvnGit(lkgr_svn)
    133     if not self["lkgr"]:
    134       print "Couldn't find git hash for lkgr %s" % lkgr_svn
    135       self.CommonCleanup()
    136       return True
    137 
    138     lkgr_utc_time = int(self.GitLog(n=1, format="%at", git_hash=self["lkgr"]))
    139     current_utc_time = self._side_effect_handler.GetUTCStamp()
    140 
    141     if current_utc_time < lkgr_utc_time + 10800:
    142       print "Candidate lkgr %s is too recent for tagging." % lkgr_svn
    143       self.CommonCleanup()
    144       return True
    145 
    146     print "Tagging revision %s with %s" % (lkgr_svn, self["candidate_version"])
    147 
    148 
    149 class MakeTag(Step):
    150   MESSAGE = "Tag the version."
    151 
    152   def RunStep(self):
    153     if not self._options.dry_run:
    154       self.GitReset(self["lkgr"])
    155       # FIXME(machenbach): Make this work with the git repo.
    156       self.vc.Tag(self["candidate_version"],
    157                   "svn/bleeding_edge",
    158                   "This won't work!")
    159 
    160 
    161 class CleanUp(Step):
    162   MESSAGE = "Clean up."
    163 
    164   def RunStep(self):
    165     self.CommonCleanup()
    166 
    167 
    168 class AutoTag(ScriptsBase):
    169   def _PrepareOptions(self, parser):
    170     parser.add_argument("--dry_run", help="Don't tag the new version.",
    171                         default=False, action="store_true")
    172 
    173   def _ProcessOptions(self, options):  # pragma: no cover
    174     if not options.dry_run and not options.author:
    175       print "Specify your chromium.org email with -a"
    176       return False
    177     options.wait_for_lgtm = False
    178     options.force_readline_defaults = True
    179     options.force_upload = True
    180     return True
    181 
    182   def _Config(self):
    183     return {
    184       "BRANCHNAME": "auto-tag-v8",
    185       "PERSISTFILE_BASENAME": "/tmp/v8-auto-tag-tempfile",
    186     }
    187 
    188   def _Steps(self):
    189     return [
    190       Preparation,
    191       GetTags,
    192       GetOldestUntaggedVersion,
    193       GetLKGRs,
    194       CalculateTagRevision,
    195       MakeTag,
    196       CleanUp,
    197     ]
    198 
    199 
    200 if __name__ == "__main__":  # pragma: no cover
    201   sys.exit(AutoTag().Run())
    202