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