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