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