From 0b246776ec886eb308b5fb83bb056c3dc883a2d6 Mon Sep 17 00:00:00 2001 From: "machenbach@chromium.org" Date: Wed, 2 Apr 2014 06:59:25 +0000 Subject: [PATCH] Implement auto-roll script. 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 | 107 +++++++++++++++++++++++++ tools/push-to-trunk/common_includes.py | 12 +-- tools/push-to-trunk/test_scripts.py | 67 +++++++++++++++- 3 files changed, 180 insertions(+), 6 deletions(-) create mode 100644 tools/push-to-trunk/auto_roll.py diff --git a/tools/push-to-trunk/auto_roll.py b/tools/push-to-trunk/auto_roll.py new file mode 100644 index 0000000000..d2ca52a362 --- /dev/null +++ b/tools/push-to-trunk/auto_roll.py @@ -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()) diff --git a/tools/push-to-trunk/common_includes.py b/tools/push-to-trunk/common_includes.py index 39b6891342..6368a279f2 100644 --- a/tools/push-to-trunk/common_includes.py +++ b/tools/push-to-trunk/common_includes.py @@ -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): diff --git a/tools/push-to-trunk/test_scripts.py b/tools/push-to-trunk/test_scripts.py index 9107db97ec..f1fe46f106 100644 --- a/tools/push-to-trunk/test_scripts.py +++ b/tools/push-to-trunk/test_scripts.py @@ -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()