Home | History | Annotate | Download | only in release
      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