auto_tag.py 5.73 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
#!/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 *


class Preparation(Step):
  MESSAGE = "Preparation."

  def RunStep(self):
16 17 18 19 20
    # TODO(machenbach): Remove after the git switch.
    if self.Config("PERSISTFILE_BASENAME") == "/tmp/v8-auto-tag-tempfile":
      print "This script is disabled until after the v8 git migration."
      return True

21 22 23
    self.CommonPrepare()
    self.PrepareBranch()
    self.GitCheckout("master")
24
    self.vc.Pull()
25 26 27 28 29 30


class GetTags(Step):
  MESSAGE = "Get all V8 tags."

  def RunStep(self):
31
    self.GitCreateBranch(self._config["BRANCHNAME"])
32
    self["tags"] = self.vc.GetTags()
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49


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.
50
      if not self.GitCheckoutFileSafe(VERSION_FILE, git_hash):
51 52 53 54 55 56 57 58 59 60
        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.
61
      self.GitCheckoutFileSafe(VERSION_FILE, "HEAD")
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111

      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
112
    return None
113 114 115

  def RunStep(self):
    # Get the lkgr after the tag candidate and before the next tag candidate.
116
    candidate_svn = self.vc.GitSvn(self["candidate"])
117
    if self["next"]:
118
      next_svn = self.vc.GitSvn(self["next"])
119 120 121 122 123 124 125 126 127 128 129 130 131
    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.
132
    self["lkgr"] = self.vc.SvnGit(lkgr_svn)
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
    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"])
155
      # FIXME(machenbach): Make this work with the git repo.
156 157 158
      self.vc.Tag(self["candidate_version"],
                  "svn/bleeding_edge",
                  "This won't work!")
159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181


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

182 183 184 185 186 187
  def _Config(self):
    return {
      "BRANCHNAME": "auto-tag-v8",
      "PERSISTFILE_BASENAME": "/tmp/v8-auto-tag-tempfile",
    }

188 189 190 191 192 193 194 195 196 197 198 199 200
  def _Steps(self):
    return [
      Preparation,
      GetTags,
      GetOldestUntaggedVersion,
      GetLKGRs,
      CalculateTagRevision,
      MakeTag,
      CleanUp,
    ]


if __name__ == "__main__":  # pragma: no cover
201
  sys.exit(AutoTag().Run())