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;luci.chromium.try:linux_optional_gpu_tests_rel;luci.chromium.try:mac_optional_gpu_tests_rel;luci.chromium.try:win_optional_gpu_tests_rel;luci.chromium.try:android_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 setdep 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("gclient", "setdep -r src/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 cq_dry_run=self._options.use_dry_run, 163 cwd=cwd) 164 print "CL uploaded." 165 else: 166 print "Dry run - don't upload." 167 168 self.GitCheckout("master", cwd=cwd) 169 self.GitDeleteBranch("work-branch", cwd=cwd) 170 171class CleanUp(Step): 172 MESSAGE = "Done!" 173 174 def RunStep(self): 175 self['json_output']['monitoring_state'] = 'success' 176 print("Congratulations, you have successfully rolled %s into " 177 "Chromium." 178 % self["roll"]) 179 180 # Clean up all temporary files. 181 Command("rm", "-f %s*" % self._config["PERSISTFILE_BASENAME"]) 182 183 184class AutoRoll(ScriptsBase): 185 def _PrepareOptions(self, parser): 186 parser.add_argument("-c", "--chromium", required=True, 187 help=("The path to your Chromium src/ " 188 "directory to automate the V8 roll.")) 189 parser.add_argument("--last-roll", 190 help="The git commit ID of the last rolled version. " 191 "Auto-detected if not specified.") 192 parser.add_argument("--max-age", default=7, type=int, 193 help="Maximum age in days of the latest release.") 194 parser.add_argument("--revision", 195 help="Revision to roll. Auto-detected if not " 196 "specified."), 197 parser.add_argument("--roll", help="Deprecated.", 198 default=True, action="store_true") 199 group = parser.add_mutually_exclusive_group() 200 group.add_argument("--use-commit-queue", 201 help="Trigger the CQ full run on upload.", 202 default=False, action="store_true") 203 group.add_argument("--use-dry-run", 204 help="Trigger the CQ dry run on upload.", 205 default=True, action="store_true") 206 207 def _ProcessOptions(self, options): # pragma: no cover 208 if not options.author or not options.reviewer: 209 print "A reviewer (-r) and an author (-a) are required." 210 return False 211 212 options.requires_editor = False 213 options.force = True 214 options.manual = False 215 return True 216 217 def _Config(self): 218 return { 219 "PERSISTFILE_BASENAME": "/tmp/v8-chromium-roll-tempfile", 220 } 221 222 def _Steps(self): 223 return [ 224 Preparation, 225 DetectLastRoll, 226 DetectRevisionToRoll, 227 PrepareRollCandidate, 228 SwitchChromium, 229 UpdateChromiumCheckout, 230 UploadCL, 231 CleanUp, 232 ] 233 234 235if __name__ == "__main__": # pragma: no cover 236 sys.exit(AutoRoll().Run()) 237