auto_tag.py 5.8 KB
Newer Older
1 2 3 4 5
#!/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.

6 7 8
# for py2/py3 compatibility
from __future__ import print_function

9 10 11 12 13 14 15 16 17 18
import argparse
import sys

from common_includes import *


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

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

24 25 26
    self.CommonPrepare()
    self.PrepareBranch()
    self.GitCheckout("master")
27
    self.vc.Pull()
28 29 30 31 32 33


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

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


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.
53
      if not self.GitCheckoutFileSafe(VERSION_FILE, git_hash):
54 55 56 57 58 59 60 61 62 63
        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.
64
      self.GitCheckoutFileSafe(VERSION_FILE, "HEAD")
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85

      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"]:
86
      print("Nothing found to tag.")
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 112 113 114
      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
115
    return None
116 117 118

  def RunStep(self):
    # Get the lkgr after the tag candidate and before the next tag candidate.
119
    candidate_svn = self.vc.GitSvn(self["candidate"])
120
    if self["next"]:
121
      next_svn = self.vc.GitSvn(self["next"])
122 123 124 125
    else:
      # Don't include the version change commit itself if there is no upper
      # limit yet.
      candidate_svn =  str(int(candidate_svn) + 1)
126
      next_svn = sys.maxsize
127 128 129
    lkgr_svn = self.LastLKGR(candidate_svn, next_svn)

    if not lkgr_svn:
130
      print("There is no lkgr since the candidate version yet.")
131 132 133 134
      self.CommonCleanup()
      return True

    # Let's check if the lkgr is at least three hours old.
135
    self["lkgr"] = self.vc.SvnGit(lkgr_svn)
136
    if not self["lkgr"]:
137
      print("Couldn't find git hash for lkgr %s" % lkgr_svn)
138 139 140 141 142 143 144
      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:
145
      print("Candidate lkgr %s is too recent for tagging." % lkgr_svn)
146 147 148
      self.CommonCleanup()
      return True

149
    print("Tagging revision %s with %s" % (lkgr_svn, self["candidate_version"]))
150 151 152 153 154 155 156 157


class MakeTag(Step):
  MESSAGE = "Tag the version."

  def RunStep(self):
    if not self._options.dry_run:
      self.GitReset(self["lkgr"])
158
      # FIXME(machenbach): Make this work with the git repo.
159 160 161
      self.vc.Tag(self["candidate_version"],
                  "svn/bleeding_edge",
                  "This won't work!")
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177


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:
178
      print("Specify your chromium.org email with -a")
179 180 181 182 183 184
      return False
    options.wait_for_lgtm = False
    options.force_readline_defaults = True
    options.force_upload = True
    return True

185 186 187 188 189 190
  def _Config(self):
    return {
      "BRANCHNAME": "auto-tag-v8",
      "PERSISTFILE_BASENAME": "/tmp/v8-auto-tag-tempfile",
    }

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


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