Implement auto-roll script.
authormachenbach@chromium.org <machenbach@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Wed, 2 Apr 2014 06:59:25 +0000 (06:59 +0000)
committermachenbach@chromium.org <machenbach@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Wed, 2 Apr 2014 06:59:25 +0000 (06:59 +0000)
This script will (1) check if there is an active roll on rietveld (2) check if there is a trunk revision ready to be rolled and (3) call the chromium_roll script, creating a roll CL. The script will be called regularly through a cron job.

BUG=
R=jarin@chromium.org

Review URL: https://codereview.chromium.org/212983003

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@20422 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

tools/push-to-trunk/auto_roll.py [new file with mode: 0644]
tools/push-to-trunk/common_includes.py
tools/push-to-trunk/test_scripts.py

diff --git a/tools/push-to-trunk/auto_roll.py b/tools/push-to-trunk/auto_roll.py
new file mode 100644 (file)
index 0000000..d2ca52a
--- /dev/null
@@ -0,0 +1,107 @@
+#!/usr/bin/env python
+# Copyright 2014 the V8 project authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import argparse
+import json
+import os
+import sys
+import urllib
+
+from common_includes import *
+import chromium_roll
+
+CR_DEPS_URL = 'http://src.chromium.org/svn/trunk/src/DEPS'
+
+class CheckActiveRoll(Step):
+  MESSAGE = "Check active roll."
+
+  @staticmethod
+  def ContainsChromiumRoll(changes):
+    for change in changes:
+      if change["subject"].startswith("Update V8 to"):
+        return True
+    return False
+
+  def RunStep(self):
+    params = {
+      "closed": 3,
+      "owner": self._options.author,
+      "limit": 30,
+      "format": "json",
+    }
+    params = urllib.urlencode(params)
+    search_url = "https://codereview.chromium.org/search"
+    result = self.ReadURL(search_url, params, wait_plan=[5, 20])
+    if self.ContainsChromiumRoll(json.loads(result)["results"]):
+      print "Stop due to existing Chromium roll."
+      return True
+
+
+class DetectLastPush(Step):
+  MESSAGE = "Detect commit ID of the last push to trunk."
+
+  def RunStep(self):
+    push_hash = self.FindLastTrunkPush()
+    self["last_push"] = self.GitSVNFindSVNRev(push_hash)
+
+
+class DetectLastRoll(Step):
+  MESSAGE = "Detect commit ID of the last Chromium roll."
+
+  def RunStep(self):
+    # Interpret the DEPS file to retrieve the v8 revision.
+    Var = lambda var: '%s'
+    exec(self.ReadURL(CR_DEPS_URL))
+    last_roll = vars['v8_revision']
+    if last_roll >= self["last_push"]:
+      print("There is no newer v8 revision than the one in Chromium (%s)."
+            % last_roll)
+      return True
+
+
+class RollChromium(Step):
+  MESSAGE = "Roll V8 into Chromium."
+
+  def RunStep(self):
+    if self._options.roll:
+      R = chromium_roll.ChromiumRoll
+      self._side_effect_handler.Call(
+          R(chromium_roll.CONFIG, self._side_effect_handler).Run,
+          ["--author", self._options.author,
+           "--reviewer", self._options.reviewer,
+           "--chromium", self._options.chromium,
+           "--force"])
+
+
+class AutoRoll(ScriptsBase):
+  def _PrepareOptions(self, parser):
+    group = parser.add_mutually_exclusive_group()
+    parser.add_argument("-c", "--chromium", required=True,
+                        help=("The path to your Chromium src/ "
+                              "directory to automate the V8 roll."))
+    parser.add_argument("--roll",
+                        help="Make Chromium roll. Dry run if unspecified.",
+                        default=False, action="store_true")
+
+  def _ProcessOptions(self, options):  # pragma: no cover
+    if not options.reviewer:
+      print "A reviewer (-r) is required."
+      return False
+    if not options.author:
+      print "An author (-a) is required."
+      return False
+    return True
+
+  def _Steps(self):
+    return [
+      CheckActiveRoll,
+      DetectLastPush,
+      DetectLastRoll,
+      RollChromium,
+    ]
+
+
+if __name__ == "__main__":  # pragma: no cover
+  sys.exit(AutoRoll(CONFIG).Run())
index 39b6891..6368a27 100644 (file)
@@ -257,10 +257,11 @@ class Step(GitRecipesMixin):
       return
 
     print ">>> Step %d: %s" % (self._number, self._text)
-    self.RunStep()
-
-    # Persist state.
-    TextToFile(json.dumps(self._state), state_file)
+    try:
+      return self.RunStep()
+    finally:
+      # Persist state.
+      TextToFile(json.dumps(self._state), state_file)
 
   def RunStep(self):  # pragma: no cover
     raise NotImplementedError
@@ -555,7 +556,8 @@ class ScriptsBase(object):
       steps.append(MakeStep(step_class, number, self._state, self._config,
                             options, self._side_effect_handler))
     for step in steps[options.step:]:
-      step.Run()
+      if step.Run():
+        return 1
     return 0
 
   def Run(self, args=None):
index 9107db9..f1fe46f 100644 (file)
@@ -34,6 +34,7 @@ import unittest
 import auto_push
 from auto_push import CheckLastPush
 from auto_push import SETTINGS_LOCATION
+import auto_roll
 import common_includes
 from common_includes import *
 import merge_to_branch
@@ -261,7 +262,7 @@ class SimpleMock(object):
     # arguments.
     if len(args) > len(expected_call['args']):
       raise NoRetryException("When calling %s with arguments, the "
-          "expectations must consist of at least as many arguments.")
+          "expectations must consist of at least as many arguments." % name)
 
     # Compare expected and actual arguments.
     for (expected_arg, actual_arg) in zip(expected_call['args'], args):
@@ -908,6 +909,70 @@ Performance and stability improvements on all platforms.""", commit)
       auto_push.AutoPush(TEST_CONFIG, self).Run(AUTO_PUSH_ARGS)
     self.assertRaises(Exception, RunAutoPush)
 
+  def testAutoRollExistingRoll(self):
+    self.ExpectReadURL([
+      URL("https://codereview.chromium.org/search",
+          "owner=author%40chromium.org&limit=30&closed=3&format=json",
+          ("{\"results\": [{\"subject\": \"different\"},"
+           "{\"subject\": \"Update V8 to Version...\"}]}")),
+    ])
+
+    result = auto_roll.AutoRoll(TEST_CONFIG, self).Run(
+        AUTO_PUSH_ARGS + ["-c", TEST_CONFIG[CHROMIUM]])
+    self.assertEquals(1, result)
+
+  # Snippet from the original DEPS file.
+  FAKE_DEPS = """
+vars = {
+  "v8_revision": "123455",
+}
+deps = {
+  "src/v8":
+    (Var("googlecode_url") % "v8") + "/" + Var("v8_branch") + "@" +
+    Var("v8_revision"),
+}
+"""
+
+  def testAutoRollUpToDate(self):
+    self.ExpectReadURL([
+      URL("https://codereview.chromium.org/search",
+          "owner=author%40chromium.org&limit=30&closed=3&format=json",
+          ("{\"results\": [{\"subject\": \"different\"}]}")),
+      URL("http://src.chromium.org/svn/trunk/src/DEPS",
+          self.FAKE_DEPS),
+    ])
+
+    self.ExpectGit([
+      Git(("log -1 --format=%H --grep="
+           "\"^Version [[:digit:]]*\.[[:digit:]]*\.[[:digit:]]* (based\" "
+           "svn/trunk"), "push_hash\n"),
+      Git("svn find-rev push_hash", "123455\n"),
+    ])
+
+    result = auto_roll.AutoRoll(TEST_CONFIG, self).Run(
+        AUTO_PUSH_ARGS + ["-c", TEST_CONFIG[CHROMIUM]])
+    self.assertEquals(1, result)
+
+  def testAutoRoll(self):
+    self.ExpectReadURL([
+      URL("https://codereview.chromium.org/search",
+          "owner=author%40chromium.org&limit=30&closed=3&format=json",
+          ("{\"results\": [{\"subject\": \"different\"}]}")),
+      URL("http://src.chromium.org/svn/trunk/src/DEPS",
+          self.FAKE_DEPS),
+    ])
+
+    self.ExpectGit([
+      Git(("log -1 --format=%H --grep="
+           "\"^Version [[:digit:]]*\.[[:digit:]]*\.[[:digit:]]* (based\" "
+           "svn/trunk"), "push_hash\n"),
+      Git("svn find-rev push_hash", "123456\n"),
+    ])
+
+    result = auto_roll.AutoRoll(TEST_CONFIG, self).Run(
+        AUTO_PUSH_ARGS + ["-c", TEST_CONFIG[CHROMIUM], "--roll"])
+    self.assertEquals(0, result)
+
   def testMergeToBranch(self):
     TEST_CONFIG[ALREADY_MERGING_SENTINEL_FILE] = self.MakeEmptyTempFile()
     TEST_CONFIG[DOT_GIT_LOCATION] = self.MakeEmptyTempFile()