Add automatic tag script.

The script can be run in a cron job to automatically tag
lkgrs.

BUG=391261
LOG=n
TEST=tools/push-to-trunk/script_test.py
TEST=tools/push-to-trunk/auto_tag.py --dry_run
R=jarin@chromium.org

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

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@22477 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
machenbach@chromium.org 2014-07-18 12:16:14 +00:00
parent 4c136e0baa
commit d9fa8f77e8
4 changed files with 261 additions and 0 deletions

200
tools/push-to-trunk/auto_tag.py Executable file
View File

@ -0,0 +1,200 @@
#!/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 sys
from common_includes import *
CONFIG = {
BRANCHNAME: "auto-tag-v8",
PERSISTFILE_BASENAME: "/tmp/v8-auto-tag-tempfile",
DOT_GIT_LOCATION: ".git",
VERSION_FILE: "src/version.cc",
}
class Preparation(Step):
MESSAGE = "Preparation."
def RunStep(self):
self.CommonPrepare()
self.PrepareBranch()
self.GitCheckout("master")
self.GitSVNRebase()
class GetTags(Step):
MESSAGE = "Get all V8 tags."
def RunStep(self):
self.GitCreateBranch(self._config[BRANCHNAME])
# Get remote tags.
tags = filter(lambda s: re.match(r"^svn/tags/[\d+\.]+$", s),
self.GitRemotes())
# Remove 'svn/tags/' prefix.
self["tags"] = map(lambda s: s[9:], tags)
class GetOldestUntaggedVersion(Step):
MESSAGE = "Check if there's a version on bleeding edge without a tag."
def RunStep(self):
tags = set(self["tags"])
self["candidate"] = None
self["candidate_version"] = None
self["next"] = None
self["next_version"] = None
# Iterate backwards through all automatic version updates.
for git_hash in self.GitLog(
format="%H", grep="\\[Auto\\-roll\\] Bump up version to").splitlines():
# Get the version.
if not self.GitCheckoutFileSafe(self._config[VERSION_FILE], git_hash):
continue
self.ReadAndPersistVersion()
version = self.ArrayToVersion("")
# Strip off trailing patch level (tags don't include tag level 0).
if version.endswith(".0"):
version = version[:-2]
# Clean up checked-out version file.
self.GitCheckoutFileSafe(self._config[VERSION_FILE], "HEAD")
if version in tags:
if self["candidate"]:
# Revision "git_hash" is tagged already and "candidate" was the next
# newer revision without a tag.
break
else:
print("Stop as %s is the latest version and it has been tagged." %
version)
self.CommonCleanup()
return True
else:
# This is the second oldest version without a tag.
self["next"] = self["candidate"]
self["next_version"] = self["candidate_version"]
# This is the oldest version without a tag.
self["candidate"] = git_hash
self["candidate_version"] = version
if not self["candidate"] or not self["candidate_version"]:
print "Nothing found to tag."
self.CommonCleanup()
return True
print("Candidate for tagging is %s with version %s" %
(self["candidate"], self["candidate_version"]))
class GetLKGRs(Step):
MESSAGE = "Get the last lkgrs."
def RunStep(self):
revision_url = "https://v8-status.appspot.com/revisions?format=json"
status_json = self.ReadURL(revision_url, wait_plan=[5, 20])
self["lkgrs"] = [entry["revision"]
for entry in json.loads(status_json) if entry["status"]]
class CalculateTagRevision(Step):
MESSAGE = "Calculate the revision to tag."
def LastLKGR(self, min_rev, max_rev):
"""Finds the newest lkgr between min_rev (inclusive) and max_rev
(exclusive).
"""
for lkgr in self["lkgrs"]:
# LKGRs are reverse sorted.
if int(min_rev) <= int(lkgr) and int(lkgr) < int(max_rev):
return lkgr
return None
def RunStep(self):
# Get the lkgr after the tag candidate and before the next tag candidate.
candidate_svn = self.GitSVNFindSVNRev(self["candidate"])
if self["next"]:
next_svn = self.GitSVNFindSVNRev(self["next"])
else:
# Don't include the version change commit itself if there is no upper
# limit yet.
candidate_svn = str(int(candidate_svn) + 1)
next_svn = sys.maxint
lkgr_svn = self.LastLKGR(candidate_svn, next_svn)
if not lkgr_svn:
print "There is no lkgr since the candidate version yet."
self.CommonCleanup()
return True
# Let's check if the lkgr is at least three hours old.
self["lkgr"] = self.GitSVNFindGitHash(lkgr_svn)
if not self["lkgr"]:
print "Couldn't find git hash for lkgr %s" % lkgr_svn
self.CommonCleanup()
return True
lkgr_utc_time = int(self.GitLog(n=1, format="%at", git_hash=self["lkgr"]))
current_utc_time = self._side_effect_handler.GetUTCStamp()
if current_utc_time < lkgr_utc_time + 10800:
print "Candidate lkgr %s is too recent for tagging." % lkgr_svn
self.CommonCleanup()
return True
print "Tagging revision %s with %s" % (lkgr_svn, self["candidate_version"])
class MakeTag(Step):
MESSAGE = "Tag the version."
def RunStep(self):
if not self._options.dry_run:
self.GitReset(self["lkgr"])
self.GitSVNTag(self["candidate_version"])
class CleanUp(Step):
MESSAGE = "Clean up."
def RunStep(self):
self.CommonCleanup()
class AutoTag(ScriptsBase):
def _PrepareOptions(self, parser):
parser.add_argument("--dry_run", help="Don't tag 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,
GetTags,
GetOldestUntaggedVersion,
GetLKGRs,
CalculateTagRevision,
MakeTag,
CleanUp,
]
if __name__ == "__main__": # pragma: no cover
sys.exit(AutoTag(CONFIG).Run())

View File

@ -244,6 +244,9 @@ class SideEffectHandler(object): # pragma: no cover
def GetDate(self):
return datetime.date.today().strftime("%Y-%m-%d")
def GetUTCStamp(self):
time.mktime(datetime.datetime.utcnow().timetuple())
DEFAULT_SIDE_EFFECT_HANDLER = SideEffectHandler()

View File

@ -185,6 +185,9 @@ class GitRecipesMixin(object):
def GitSVNFetch(self):
self.Git("svn fetch")
def GitSVNRebase(self):
self.Git("svn rebase")
# TODO(machenbach): Unused? Remove.
@Strip
def GitSVNLog(self):

View File

@ -52,6 +52,7 @@ import bump_up_version
from bump_up_version import BumpUpVersion
from bump_up_version import LastChangeBailout
from bump_up_version import LKGRVersionUpToDateBailout
from auto_tag import AutoTag
TEST_CONFIG = {
@ -401,6 +402,9 @@ class ScriptTest(unittest.TestCase):
def GetDate(self):
return "1999-07-31"
def GetUTCStamp(self):
return "100000"
def ExpectGit(self, *args):
"""Convenience wrapper."""
self._git_mock.Expect(*args)
@ -1323,6 +1327,57 @@ LOG=N
BumpUpVersion(TEST_CONFIG, self).Run(["-a", "author@chromium.org"])
def testAutoTag(self):
TEST_CONFIG[VERSION_FILE] = self.MakeEmptyTempFile()
self.WriteFakeVersionFile()
def ResetVersion(minor, build, patch=0):
return lambda: self.WriteFakeVersionFile(minor=minor,
build=build,
patch=patch)
self.ExpectGit([
Git("status -s -uno", ""),
Git("status -s -b -uno", "## some_branch\n"),
Git("svn fetch", ""),
Git("branch", " branch1\n* branch2\n"),
Git("checkout -f master", ""),
Git("svn rebase", ""),
Git("checkout -b %s" % TEST_CONFIG[BRANCHNAME], "",
cb=ResetVersion(4, 5)),
Git("branch -r", "svn/tags/3.4.2\nsvn/tags/3.2.1.0\nsvn/branches/3.4"),
Git("log --format=%H --grep=\"\\[Auto\\-roll\\] Bump up version to\"",
"hash125\nhash118\nhash111\nhash101"),
Git("checkout -f hash125 -- %s" % TEST_CONFIG[VERSION_FILE], "",
cb=ResetVersion(4, 4)),
Git("checkout -f HEAD -- %s" % TEST_CONFIG[VERSION_FILE], "",
cb=ResetVersion(4, 5)),
Git("checkout -f hash118 -- %s" % TEST_CONFIG[VERSION_FILE], "",
cb=ResetVersion(4, 3)),
Git("checkout -f HEAD -- %s" % TEST_CONFIG[VERSION_FILE], "",
cb=ResetVersion(4, 5)),
Git("checkout -f hash111 -- %s" % TEST_CONFIG[VERSION_FILE], "",
cb=ResetVersion(4, 2)),
Git("checkout -f HEAD -- %s" % TEST_CONFIG[VERSION_FILE], "",
cb=ResetVersion(4, 5)),
Git("svn find-rev hash118", "118"),
Git("svn find-rev hash125", "125"),
Git("svn find-rev r123", "hash123"),
Git("log -1 --format=%at hash123", "1"),
Git("reset --hard hash123", ""),
Git("svn tag 3.4.3 -m \"Tagging version 3.4.3\"", ""),
Git("checkout -f some_branch", ""),
Git("branch -D %s" % TEST_CONFIG[BRANCHNAME], ""),
])
self.ExpectReadURL([
URL("https://v8-status.appspot.com/revisions?format=json",
"[{\"revision\": \"123\", \"status\": true},"
"{\"revision\": \"112\", \"status\": true}]"),
])
AutoTag(TEST_CONFIG, self).Run(["-a", "author@chromium.org"])
# Test that we bail out if the last change was a version change.
def testBumpUpVersionBailout1(self):
TEST_CONFIG[VERSION_FILE] = self.MakeEmptyTempFile()