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 os 8 import sys 9 10 from common_includes import * 11 12 ROLL_SUMMARY = ("Summary of changes available at:\n" 13 "https://chromium.googlesource.com/v8/v8/+log/%s..%s") 14 15 ISSUE_MSG = ( 16 """Please follow these instructions for assigning/CC'ing issues: 17 https://github.com/v8/v8/wiki/Triaging%20issues 18 19 Please close rolling in case of a roll revert: 20 https://v8-roll.appspot.com/ 21 This only works with a Google account.""") 22 23 class Preparation(Step): 24 MESSAGE = "Preparation." 25 26 def RunStep(self): 27 self['json_output']['monitoring_state'] = 'preparation' 28 # Update v8 remote tracking branches. 29 self.GitFetchOrigin() 30 self.Git("fetch origin +refs/tags/*:refs/tags/*") 31 32 33 class DetectLastRoll(Step): 34 MESSAGE = "Detect commit ID of the last Chromium roll." 35 36 def RunStep(self): 37 self['json_output']['monitoring_state'] = 'detect_last_roll' 38 self["last_roll"] = self._options.last_roll 39 if not self["last_roll"]: 40 # Interpret the DEPS file to retrieve the v8 revision. 41 # TODO(machenbach): This should be part or the roll-deps api of 42 # depot_tools. 43 Var = lambda var: '%s' 44 exec(FileToText(os.path.join(self._options.chromium, "DEPS"))) 45 46 # The revision rolled last. 47 self["last_roll"] = vars['v8_revision'] 48 self["last_version"] = self.GetVersionTag(self["last_roll"]) 49 assert self["last_version"], "The last rolled v8 revision is not tagged." 50 51 52 class DetectRevisionToRoll(Step): 53 MESSAGE = "Detect commit ID of the V8 revision to roll." 54 55 def RunStep(self): 56 self['json_output']['monitoring_state'] = 'detect_revision' 57 self["roll"] = self._options.revision 58 if self["roll"]: 59 # If the revision was passed on the cmd line, continue script execution 60 # in the next step. 61 return False 62 63 # The revision that should be rolled. Check for the latest of the most 64 # recent releases based on commit timestamp. 65 revisions = self.GetRecentReleases( 66 max_age=self._options.max_age * DAY_IN_SECONDS) 67 assert revisions, "Didn't find any recent release." 68 69 # There must be some progress between the last roll and the new candidate 70 # revision (i.e. we don't go backwards). The revisions are ordered newest 71 # to oldest. It is possible that the newest timestamp has no progress 72 # compared to the last roll, i.e. if the newest release is a cherry-pick 73 # on a release branch. Then we look further. 74 for revision in revisions: 75 version = self.GetVersionTag(revision) 76 assert version, "Internal error. All recent releases should have a tag" 77 78 if SortingKey(self["last_version"]) < SortingKey(version): 79 self["roll"] = revision 80 break 81 else: 82 print("There is no newer v8 revision than the one in Chromium (%s)." 83 % self["last_roll"]) 84 self['json_output']['monitoring_state'] = 'up_to_date' 85 return True 86 87 88 class PrepareRollCandidate(Step): 89 MESSAGE = "Robustness checks of the roll candidate." 90 91 def RunStep(self): 92 self['json_output']['monitoring_state'] = 'prepare_candidate' 93 self["roll_title"] = self.GitLog(n=1, format="%s", 94 git_hash=self["roll"]) 95 96 # Make sure the last roll and the roll candidate are releases. 97 version = self.GetVersionTag(self["roll"]) 98 assert version, "The revision to roll is not tagged." 99 version = self.GetVersionTag(self["last_roll"]) 100 assert version, "The revision used as last roll is not tagged." 101 102 103 class SwitchChromium(Step): 104 MESSAGE = "Switch to Chromium checkout." 105 106 def RunStep(self): 107 self['json_output']['monitoring_state'] = 'switch_chromium' 108 cwd = self._options.chromium 109 self.InitialEnvironmentChecks(cwd) 110 # Check for a clean workdir. 111 if not self.GitIsWorkdirClean(cwd=cwd): # pragma: no cover 112 self.Die("Workspace is not clean. Please commit or undo your changes.") 113 # Assert that the DEPS file is there. 114 if not os.path.exists(os.path.join(cwd, "DEPS")): # pragma: no cover 115 self.Die("DEPS file not present.") 116 117 118 class UpdateChromiumCheckout(Step): 119 MESSAGE = "Update the checkout and create a new branch." 120 121 def RunStep(self): 122 self['json_output']['monitoring_state'] = 'update_chromium' 123 cwd = self._options.chromium 124 self.GitCheckout("master", cwd=cwd) 125 self.DeleteBranch("work-branch", cwd=cwd) 126 self.Command("gclient", "sync --nohooks", cwd=cwd) 127 self.GitPull(cwd=cwd) 128 129 # Update v8 remotes. 130 self.GitFetchOrigin() 131 132 self.GitCreateBranch("work-branch", cwd=cwd) 133 134 135 class UploadCL(Step): 136 MESSAGE = "Create and upload CL." 137 138 def RunStep(self): 139 self['json_output']['monitoring_state'] = 'upload' 140 cwd = self._options.chromium 141 # Patch DEPS file. 142 if self.Command("roll-dep-svn", "v8 %s" % 143 self["roll"], cwd=cwd) is None: 144 self.Die("Failed to create deps for %s" % self["roll"]) 145 146 message = [] 147 message.append("Update V8 to %s." % self["roll_title"].lower()) 148 149 message.append( 150 ROLL_SUMMARY % (self["last_roll"][:8], self["roll"][:8])) 151 152 message.append(ISSUE_MSG) 153 154 message.append("TBR=%s" % self._options.reviewer) 155 self.GitCommit("\n\n".join(message), author=self._options.author, cwd=cwd) 156 if not self._options.dry_run: 157 self.GitUpload(author=self._options.author, 158 force=True, 159 cq=self._options.use_commit_queue, 160 cwd=cwd) 161 print "CL uploaded." 162 else: 163 print "Dry run - don't upload." 164 165 self.GitCheckout("master", cwd=cwd) 166 self.GitDeleteBranch("work-branch", cwd=cwd) 167 168 class CleanUp(Step): 169 MESSAGE = "Done!" 170 171 def RunStep(self): 172 self['json_output']['monitoring_state'] = 'success' 173 print("Congratulations, you have successfully rolled %s into " 174 "Chromium." 175 % self["roll"]) 176 177 # Clean up all temporary files. 178 Command("rm", "-f %s*" % self._config["PERSISTFILE_BASENAME"]) 179 180 181 class AutoRoll(ScriptsBase): 182 def _PrepareOptions(self, parser): 183 parser.add_argument("-c", "--chromium", required=True, 184 help=("The path to your Chromium src/ " 185 "directory to automate the V8 roll.")) 186 parser.add_argument("--last-roll", 187 help="The git commit ID of the last rolled version. " 188 "Auto-detected if not specified.") 189 parser.add_argument("--max-age", default=7, type=int, 190 help="Maximum age in days of the latest release.") 191 parser.add_argument("--revision", 192 help="Revision to roll. Auto-detected if not " 193 "specified."), 194 parser.add_argument("--roll", help="Deprecated.", 195 default=True, action="store_true") 196 parser.add_argument("--use-commit-queue", 197 help="Check the CQ bit on upload.", 198 default=True, action="store_true") 199 200 def _ProcessOptions(self, options): # pragma: no cover 201 if not options.author or not options.reviewer: 202 print "A reviewer (-r) and an author (-a) are required." 203 return False 204 205 options.requires_editor = False 206 options.force = True 207 options.manual = False 208 return True 209 210 def _Config(self): 211 return { 212 "PERSISTFILE_BASENAME": "/tmp/v8-chromium-roll-tempfile", 213 } 214 215 def _Steps(self): 216 return [ 217 Preparation, 218 DetectLastRoll, 219 DetectRevisionToRoll, 220 PrepareRollCandidate, 221 SwitchChromium, 222 UpdateChromiumCheckout, 223 UploadCL, 224 CleanUp, 225 ] 226 227 228 if __name__ == "__main__": # pragma: no cover 229 sys.exit(AutoRoll().Run()) 230