| OLD | NEW | 
|---|
|  | (Empty) | 
| 1 #!/usr/bin/env python |  | 
| 2 # Copyright 2013 the V8 project authors. All rights reserved. |  | 
| 3 # Redistribution and use in source and binary forms, with or without |  | 
| 4 # modification, are permitted provided that the following conditions are |  | 
| 5 # met: |  | 
| 6 # |  | 
| 7 #     * Redistributions of source code must retain the above copyright |  | 
| 8 #       notice, this list of conditions and the following disclaimer. |  | 
| 9 #     * Redistributions in binary form must reproduce the above |  | 
| 10 #       copyright notice, this list of conditions and the following |  | 
| 11 #       disclaimer in the documentation and/or other materials provided |  | 
| 12 #       with the distribution. |  | 
| 13 #     * Neither the name of Google Inc. nor the names of its |  | 
| 14 #       contributors may be used to endorse or promote products derived |  | 
| 15 #       from this software without specific prior written permission. |  | 
| 16 # |  | 
| 17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |  | 
| 18 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |  | 
| 19 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |  | 
| 20 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |  | 
| 21 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |  | 
| 22 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |  | 
| 23 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |  | 
| 24 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |  | 
| 25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |  | 
| 26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |  | 
| 27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |  | 
| 28 |  | 
| 29 import argparse |  | 
| 30 import json |  | 
| 31 import os |  | 
| 32 import re |  | 
| 33 import sys |  | 
| 34 import urllib |  | 
| 35 |  | 
| 36 from common_includes import * |  | 
| 37 import push_to_trunk |  | 
| 38 |  | 
| 39 SETTINGS_LOCATION = "SETTINGS_LOCATION" |  | 
| 40 |  | 
| 41 CONFIG = { |  | 
| 42   PERSISTFILE_BASENAME: "/tmp/v8-auto-roll-tempfile", |  | 
| 43   DOT_GIT_LOCATION: ".git", |  | 
| 44   SETTINGS_LOCATION: "~/.auto-roll", |  | 
| 45 } |  | 
| 46 |  | 
| 47 PUSH_MESSAGE_RE = re.compile(r".* \(based on bleeding_edge revision r(\d+)\)$") |  | 
| 48 |  | 
| 49 |  | 
| 50 class Preparation(Step): |  | 
| 51   MESSAGE = "Preparation." |  | 
| 52 |  | 
| 53   def RunStep(self): |  | 
| 54     self.InitialEnvironmentChecks() |  | 
| 55     self.CommonPrepare() |  | 
| 56 |  | 
| 57 |  | 
| 58 class CheckAutoRollSettings(Step): |  | 
| 59   MESSAGE = "Checking settings file." |  | 
| 60 |  | 
| 61   def RunStep(self): |  | 
| 62     settings_file = os.path.realpath(self.Config(SETTINGS_LOCATION)) |  | 
| 63     if os.path.exists(settings_file): |  | 
| 64       settings_dict = json.loads(FileToText(settings_file)) |  | 
| 65       if settings_dict.get("enable_auto_roll") is False: |  | 
| 66         self.Die("Push to trunk disabled by auto-roll settings file: %s" |  | 
| 67                  % settings_file) |  | 
| 68 |  | 
| 69 |  | 
| 70 class CheckTreeStatus(Step): |  | 
| 71   MESSAGE = "Checking v8 tree status message." |  | 
| 72 |  | 
| 73   def RunStep(self): |  | 
| 74     status_url = "https://v8-status.appspot.com/current?format=json" |  | 
| 75     status_json = self.ReadURL(status_url, wait_plan=[5, 20, 300, 300]) |  | 
| 76     self["tree_message"] = json.loads(status_json)["message"] |  | 
| 77     if re.search(r"nopush|no push", self["tree_message"], flags=re.I): |  | 
| 78       self.Die("Push to trunk disabled by tree state: %s" |  | 
| 79                % self["tree_message"]) |  | 
| 80 |  | 
| 81 |  | 
| 82 class FetchLKGR(Step): |  | 
| 83   MESSAGE = "Fetching V8 LKGR." |  | 
| 84 |  | 
| 85   def RunStep(self): |  | 
| 86     lkgr_url = "https://v8-status.appspot.com/lkgr" |  | 
| 87     # Retry several times since app engine might have issues. |  | 
| 88     self["lkgr"] = self.ReadURL(lkgr_url, wait_plan=[5, 20, 300, 300]) |  | 
| 89 |  | 
| 90 |  | 
| 91 class CheckLastPush(Step): |  | 
| 92   MESSAGE = "Checking last V8 push to trunk." |  | 
| 93 |  | 
| 94   def RunStep(self): |  | 
| 95     last_push = self.FindLastTrunkPush() |  | 
| 96 |  | 
| 97     # Retrieve the bleeding edge revision of the last push from the text in |  | 
| 98     # the push commit message. |  | 
| 99     last_push_title = self.GitLog(n=1, format="%s", git_hash=last_push) |  | 
| 100     last_push_be = PUSH_MESSAGE_RE.match(last_push_title).group(1) |  | 
| 101 |  | 
| 102     if not last_push_be:  # pragma: no cover |  | 
| 103       self.Die("Could not retrieve bleeding edge revision for trunk push %s" |  | 
| 104                % last_push) |  | 
| 105 |  | 
| 106     # TODO(machenbach): This metric counts all revisions. It could be |  | 
| 107     # improved by counting only the revisions on bleeding_edge. |  | 
| 108     if int(self["lkgr"]) - int(last_push_be) < 10:  # pragma: no cover |  | 
| 109       # This makes sure the script doesn't push twice in a row when the cron |  | 
| 110       # job retries several times. |  | 
| 111       self.Die("Last push too recently: %s" % last_push_be) |  | 
| 112 |  | 
| 113 |  | 
| 114 class PushToTrunk(Step): |  | 
| 115   MESSAGE = "Pushing to trunk if specified." |  | 
| 116 |  | 
| 117   def PushTreeStatus(self, message): |  | 
| 118     if not self._options.status_password: |  | 
| 119       print "Skipping tree status update without password file." |  | 
| 120       return |  | 
| 121     params = { |  | 
| 122       "message": message, |  | 
| 123       "username": "v8-auto-roll@chromium.org", |  | 
| 124       "password": FileToText(self._options.status_password).strip(), |  | 
| 125     } |  | 
| 126     params = urllib.urlencode(params) |  | 
| 127     print "Pushing tree status: '%s'" % message |  | 
| 128     self.ReadURL("https://v8-status.appspot.com/status", params, |  | 
| 129                  wait_plan=[5, 20]) |  | 
| 130 |  | 
| 131   def RunStep(self): |  | 
| 132     print "Pushing lkgr %s to trunk." % self["lkgr"] |  | 
| 133     self.PushTreeStatus("Tree is closed (preparing to push)") |  | 
| 134 |  | 
| 135     # TODO(machenbach): Update the script before calling it. |  | 
| 136     try: |  | 
| 137       if self._options.push: |  | 
| 138         P = push_to_trunk.PushToTrunk |  | 
| 139         self._side_effect_handler.Call( |  | 
| 140             P(push_to_trunk.CONFIG, self._side_effect_handler).Run, |  | 
| 141             ["--author", self._options.author, |  | 
| 142              "--reviewer", self._options.reviewer, |  | 
| 143              "--revision", self["lkgr"], |  | 
| 144              "--force"]) |  | 
| 145     finally: |  | 
| 146       self.PushTreeStatus(self["tree_message"]) |  | 
| 147 |  | 
| 148 |  | 
| 149 class AutoRoll(ScriptsBase): |  | 
| 150   def _PrepareOptions(self, parser): |  | 
| 151     parser.add_argument("-c", "--chromium", |  | 
| 152                         help=("Deprecated.")) |  | 
| 153     parser.add_argument("-p", "--push", |  | 
| 154                         help="Push to trunk. Dry run if unspecified.", |  | 
| 155                         default=False, action="store_true") |  | 
| 156     parser.add_argument("--status-password", |  | 
| 157                         help="A file with the password to the status app.") |  | 
| 158 |  | 
| 159   def _ProcessOptions(self, options): |  | 
| 160     if not options.author or not options.reviewer:  # pragma: no cover |  | 
| 161       print "You need to specify author and reviewer." |  | 
| 162       return False |  | 
| 163     options.requires_editor = False |  | 
| 164     return True |  | 
| 165 |  | 
| 166   def _Steps(self): |  | 
| 167     return [ |  | 
| 168       Preparation, |  | 
| 169       CheckAutoRollSettings, |  | 
| 170       CheckTreeStatus, |  | 
| 171       FetchLKGR, |  | 
| 172       CheckLastPush, |  | 
| 173       PushToTrunk, |  | 
| 174     ] |  | 
| 175 |  | 
| 176 |  | 
| 177 if __name__ == "__main__":  # pragma: no cover |  | 
| 178   sys.exit(AutoRoll(CONFIG).Run()) |  | 
| OLD | NEW | 
|---|