#!/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. """ Script for auto-increasing the version on bleeding_edge. The script can be run regularly by a cron job. It will increase the build level of the version on bleeding_edge if: - the lkgr version is smaller than the version of the latest revision, - the lkgr version is not a version change itself, - the tree is not closed for maintenance. The new version will be the maximum of the bleeding_edge and trunk versions +1. E.g. latest bleeding_edge version: 3.22.11.0 and latest trunk 3.23.0.0 gives the new version 3.23.1.0. This script requires a depot tools git checkout. I.e. 'fetch v8'. """ import argparse import os import sys from common_includes import * CONFIG = { PERSISTFILE_BASENAME: "/tmp/v8-bump-up-version-tempfile", VERSION_FILE: "src/version.cc", } VERSION_BRANCH = "auto-bump-up-version" class Preparation(Step): MESSAGE = "Preparation." def RunStep(self): # Check for a clean workdir. if not self.GitIsWorkdirClean(): # pragma: no cover # This is in case a developer runs this script on a dirty tree. self.GitStash() # TODO(machenbach): This should be called master after the git switch. self.GitCheckout("bleeding_edge") self.GitPull() # Ensure a clean version branch. self.DeleteBranch(VERSION_BRANCH) class GetCurrentBleedingEdgeVersion(Step): MESSAGE = "Get latest bleeding edge version." def RunStep(self): # TODO(machenbach): This should be called master after the git switch. self.GitCheckout("bleeding_edge") # Store latest version and revision. self.ReadAndPersistVersion() self["latest_version"] = self.ArrayToVersion("") self["latest"] = self.GitLog(n=1, format="%H") print "Bleeding edge version: %s" % self["latest_version"] # This step is pure paranoia. It forbids the script to continue if the last # commit changed version.cc. Just in case the other bailout has a bug, this # prevents the script from continuously commiting version changes. class LastChangeBailout(Step): MESSAGE = "Stop script if the last change modified the version." def RunStep(self): if self._config[VERSION_FILE] in self.GitChangedFiles(self["latest"]): print "Stop due to recent version change." return True # TODO(machenbach): Implement this for git. class FetchLKGR(Step): MESSAGE = "Fetching V8 LKGR." def RunStep(self): lkgr_url = "https://v8-status.appspot.com/lkgr" self["lkgr_svn"] = self.ReadURL(lkgr_url, wait_plan=[5]) # TODO(machenbach): Implement this for git. With a git lkgr we could simply # checkout that revision. With svn, we have to search backwards until that # revision is found. class GetLKGRVersion(Step): MESSAGE = "Get bleeding edge lkgr version." def RunStep(self): self.GitCheckout("bleeding_edge") # If the commit was made from svn, there is a mapping entry in the commit # message. self["lkgr"] = self.GitLog( grep="^git-svn-id: [^@]*@%s [A-Za-z0-9-]*$" % self["lkgr_svn"], format="%H") # FIXME(machenbach): http://crbug.com/391712 can lead to svn lkgrs on the # trunk branch (rarely). if not self["lkgr"]: # pragma: no cover self.Die("No git hash found for svn lkgr.") self.GitCreateBranch(VERSION_BRANCH, self["lkgr"]) self.ReadAndPersistVersion("lkgr_") self["lkgr_version"] = self.ArrayToVersion("lkgr_") print "LKGR version: %s" % self["lkgr_version"] # Ensure a clean version branch. self.GitCheckout("bleeding_edge") self.DeleteBranch(VERSION_BRANCH) class LKGRVersionUpToDateBailout(Step): MESSAGE = "Stop script if the lkgr has a renewed version." def RunStep(self): # If a version-change commit becomes the lkgr, don't bump up the version # again. if self._config[VERSION_FILE] in self.GitChangedFiles(self["lkgr"]): print "Stop because the lkgr is a version change itself." return True # Don't bump up the version if it got updated already after the lkgr. if SortingKey(self["lkgr_version"]) < SortingKey(self["latest_version"]): print("Stop because the latest version already changed since the lkgr " "version.") return True class GetTrunkVersion(Step): MESSAGE = "Get latest trunk version." def RunStep(self): # TODO(machenbach): This should be called trunk after the git switch. self.GitCheckout("master") self.GitPull() self.ReadAndPersistVersion("trunk_") self["trunk_version"] = self.ArrayToVersion("trunk_") print "Trunk version: %s" % self["trunk_version"] class CalculateVersion(Step): MESSAGE = "Calculate the new version." def RunStep(self): if self["lkgr_build"] == "9999": # pragma: no cover # If version control on bleeding edge was switched off, just use the last # trunk version. self["lkgr_version"] = self["trunk_version"] # The new version needs to be greater than the max on bleeding edge and # trunk. max_version = max(self["trunk_version"], self["lkgr_version"], key=SortingKey) # Strip off possible leading zeros. self["new_major"], self["new_minor"], self["new_build"], _ = ( map(str, map(int, max_version.split(".")))) self["new_build"] = str(int(self["new_build"]) + 1) self["new_patch"] = "0" self["new_version"] = ("%s.%s.%s.0" % (self["new_major"], self["new_minor"], self["new_build"])) print "New version is %s" % self["new_version"] if self._options.dry_run: # pragma: no cover print "Dry run, skipping version change." return True class CheckTreeStatus(Step): MESSAGE = "Checking v8 tree status message." def RunStep(self): status_url = "https://v8-status.appspot.com/current?format=json" status_json = self.ReadURL(status_url, wait_plan=[5, 20, 300, 300]) message = json.loads(status_json)["message"] if re.search(r"maintenance|no commits", message, flags=re.I): print "Skip version change by tree status: \"%s\"" % message return True class ChangeVersion(Step): MESSAGE = "Bump up the version." def RunStep(self): self.GitCreateBranch(VERSION_BRANCH, "bleeding_edge") self.SetVersion(self.Config(VERSION_FILE), "new_") try: self.GitCommit("[Auto-roll] Bump up version to %s\n\nTBR=%s" % (self["new_version"], self._options.author)) self.GitUpload(author=self._options.author, force=self._options.force_upload, bypass_hooks=True) self.GitDCommit() print "Successfully changed the version." finally: # Clean up. self.GitCheckout("bleeding_edge") self.DeleteBranch(VERSION_BRANCH) class BumpUpVersion(ScriptsBase): def _PrepareOptions(self, parser): parser.add_argument("--dry_run", help="Don't commit the new version.", default=False, action="store_true") def _ProcessOptions(self, options): # pragma: no cover if not options.dry_run and not options.author: print "Specify your chromium.org email with -a" return False options.wait_for_lgtm = False options.force_readline_defaults = True options.force_upload = True return True def _Steps(self): return [ Preparation, GetCurrentBleedingEdgeVersion, LastChangeBailout, FetchLKGR, GetLKGRVersion, LKGRVersionUpToDateBailout, GetTrunkVersion, CalculateVersion, CheckTreeStatus, ChangeVersion, ] if __name__ == "__main__": # pragma: no cover sys.exit(BumpUpVersion(CONFIG).Run())