11cb0ef41Sopenharmony_ci#!/usr/bin/env python3
21cb0ef41Sopenharmony_ci# Copyright 2015 the V8 project authors. All rights reserved.
31cb0ef41Sopenharmony_ci# Use of this source code is governed by a BSD-style license that can be
41cb0ef41Sopenharmony_ci# found in the LICENSE file.
51cb0ef41Sopenharmony_ci
61cb0ef41Sopenharmony_ciimport argparse
71cb0ef41Sopenharmony_ciimport os
81cb0ef41Sopenharmony_ciimport sys
91cb0ef41Sopenharmony_ciimport tempfile
101cb0ef41Sopenharmony_ci
111cb0ef41Sopenharmony_cifrom common_includes import *
121cb0ef41Sopenharmony_ci
131cb0ef41Sopenharmony_ciimport urllib.request as urllib2
141cb0ef41Sopenharmony_ci
151cb0ef41Sopenharmony_ci
161cb0ef41Sopenharmony_ciclass Preparation(Step):
171cb0ef41Sopenharmony_ci  MESSAGE = "Preparation."
181cb0ef41Sopenharmony_ci
191cb0ef41Sopenharmony_ci  def RunStep(self):
201cb0ef41Sopenharmony_ci    self.Git("fetch origin +refs/heads/*:refs/heads/*")
211cb0ef41Sopenharmony_ci    self.GitCheckout("origin/main")
221cb0ef41Sopenharmony_ci    self.DeleteBranch("work-branch")
231cb0ef41Sopenharmony_ci
241cb0ef41Sopenharmony_ci
251cb0ef41Sopenharmony_ciclass PrepareBranchRevision(Step):
261cb0ef41Sopenharmony_ci  MESSAGE = "Check from which revision to branch off."
271cb0ef41Sopenharmony_ci
281cb0ef41Sopenharmony_ci  def RunStep(self):
291cb0ef41Sopenharmony_ci    self["push_hash"] = (self._options.revision or
301cb0ef41Sopenharmony_ci                         self.GitLog(n=1, format="%H", branch="origin/main"))
311cb0ef41Sopenharmony_ci    assert self["push_hash"]
321cb0ef41Sopenharmony_ci    print("Release revision %s" % self["push_hash"])
331cb0ef41Sopenharmony_ci
341cb0ef41Sopenharmony_ci
351cb0ef41Sopenharmony_ciclass IncrementVersion(Step):
361cb0ef41Sopenharmony_ci  MESSAGE = "Increment version number."
371cb0ef41Sopenharmony_ci
381cb0ef41Sopenharmony_ci  def RunStep(self):
391cb0ef41Sopenharmony_ci    latest_version = self.GetLatestVersion()
401cb0ef41Sopenharmony_ci
411cb0ef41Sopenharmony_ci    # The version file on main can be used to bump up major/minor at
421cb0ef41Sopenharmony_ci    # branch time.
431cb0ef41Sopenharmony_ci    self.GitCheckoutFile(VERSION_FILE, self.vc.RemoteMainBranch())
441cb0ef41Sopenharmony_ci    self.ReadAndPersistVersion("main_")
451cb0ef41Sopenharmony_ci    main_version = self.ArrayToVersion("main_")
461cb0ef41Sopenharmony_ci
471cb0ef41Sopenharmony_ci    # Use the highest version from main or from tags to determine the new
481cb0ef41Sopenharmony_ci    # version.
491cb0ef41Sopenharmony_ci    authoritative_version = sorted(
501cb0ef41Sopenharmony_ci        [main_version, latest_version], key=LooseVersion)[1]
511cb0ef41Sopenharmony_ci    self.StoreVersion(authoritative_version, "authoritative_")
521cb0ef41Sopenharmony_ci
531cb0ef41Sopenharmony_ci    # Variables prefixed with 'new_' contain the new version numbers for the
541cb0ef41Sopenharmony_ci    # ongoing candidates push.
551cb0ef41Sopenharmony_ci    self["new_major"] = self["authoritative_major"]
561cb0ef41Sopenharmony_ci    self["new_minor"] = self["authoritative_minor"]
571cb0ef41Sopenharmony_ci    self["new_build"] = str(int(self["authoritative_build"]) + 1)
581cb0ef41Sopenharmony_ci
591cb0ef41Sopenharmony_ci    # Make sure patch level is 0 in a new push.
601cb0ef41Sopenharmony_ci    self["new_patch"] = "0"
611cb0ef41Sopenharmony_ci
621cb0ef41Sopenharmony_ci    # The new version is not a candidate.
631cb0ef41Sopenharmony_ci    self["new_candidate"] = "0"
641cb0ef41Sopenharmony_ci
651cb0ef41Sopenharmony_ci    self["version"] = "%s.%s.%s" % (self["new_major"],
661cb0ef41Sopenharmony_ci                                    self["new_minor"],
671cb0ef41Sopenharmony_ci                                    self["new_build"])
681cb0ef41Sopenharmony_ci
691cb0ef41Sopenharmony_ci    print ("Incremented version to %s" % self["version"])
701cb0ef41Sopenharmony_ci
711cb0ef41Sopenharmony_ci
721cb0ef41Sopenharmony_ciclass DetectLastRelease(Step):
731cb0ef41Sopenharmony_ci  MESSAGE = "Detect commit ID of last release base."
741cb0ef41Sopenharmony_ci
751cb0ef41Sopenharmony_ci  def RunStep(self):
761cb0ef41Sopenharmony_ci    self["last_push_main"] = self.GetLatestReleaseBase()
771cb0ef41Sopenharmony_ci
781cb0ef41Sopenharmony_ci
791cb0ef41Sopenharmony_ciclass DeleteBranchRef(Step):
801cb0ef41Sopenharmony_ci  MESSAGE = "Delete branch ref."
811cb0ef41Sopenharmony_ci
821cb0ef41Sopenharmony_ci  def RunStep(self):
831cb0ef41Sopenharmony_ci    cmd = "push origin :refs/heads/%s" % self["version"]
841cb0ef41Sopenharmony_ci    if self._options.dry_run:
851cb0ef41Sopenharmony_ci      print("Dry run. Command:\ngit %s" % cmd)
861cb0ef41Sopenharmony_ci    else:
871cb0ef41Sopenharmony_ci      try:
881cb0ef41Sopenharmony_ci        self.Git(cmd)
891cb0ef41Sopenharmony_ci      except Exception:
901cb0ef41Sopenharmony_ci        # Be forgiving if branch ref does not exist.
911cb0ef41Sopenharmony_ci        pass
921cb0ef41Sopenharmony_ci
931cb0ef41Sopenharmony_ci
941cb0ef41Sopenharmony_ciclass PushBranchRef(Step):
951cb0ef41Sopenharmony_ci  MESSAGE = "Create branch ref."
961cb0ef41Sopenharmony_ci
971cb0ef41Sopenharmony_ci  def RunStep(self):
981cb0ef41Sopenharmony_ci    cmd = "push origin %s:refs/heads/%s" % (self["push_hash"], self["version"])
991cb0ef41Sopenharmony_ci    if self._options.dry_run:
1001cb0ef41Sopenharmony_ci      print("Dry run. Command:\ngit %s" % cmd)
1011cb0ef41Sopenharmony_ci    else:
1021cb0ef41Sopenharmony_ci      self.Git(cmd)
1031cb0ef41Sopenharmony_ci
1041cb0ef41Sopenharmony_ci
1051cb0ef41Sopenharmony_ciclass MakeBranch(Step):
1061cb0ef41Sopenharmony_ci  MESSAGE = "Create the branch."
1071cb0ef41Sopenharmony_ci
1081cb0ef41Sopenharmony_ci  def RunStep(self):
1091cb0ef41Sopenharmony_ci    self.Git("reset --hard origin/main")
1101cb0ef41Sopenharmony_ci    self.Git("new-branch work-branch --upstream origin/%s" % self["version"])
1111cb0ef41Sopenharmony_ci    self.GitCheckoutFile(VERSION_FILE, self["latest_version"])
1121cb0ef41Sopenharmony_ci
1131cb0ef41Sopenharmony_ci
1141cb0ef41Sopenharmony_ciclass SetVersion(Step):
1151cb0ef41Sopenharmony_ci  MESSAGE = "Set correct version for candidates."
1161cb0ef41Sopenharmony_ci
1171cb0ef41Sopenharmony_ci  def RunStep(self):
1181cb0ef41Sopenharmony_ci    self.SetVersion(os.path.join(self.default_cwd, VERSION_FILE), "new_")
1191cb0ef41Sopenharmony_ci
1201cb0ef41Sopenharmony_ci
1211cb0ef41Sopenharmony_ciclass EnableMergeWatchlist(Step):
1221cb0ef41Sopenharmony_ci  MESSAGE = "Enable watchlist entry for merge notifications."
1231cb0ef41Sopenharmony_ci
1241cb0ef41Sopenharmony_ci  def RunStep(self):
1251cb0ef41Sopenharmony_ci    old_watchlist_content = FileToText(os.path.join(self.default_cwd,
1261cb0ef41Sopenharmony_ci                                                    WATCHLISTS_FILE))
1271cb0ef41Sopenharmony_ci    new_watchlist_content = re.sub("(# 'v8-merges@googlegroups\.com',)",
1281cb0ef41Sopenharmony_ci                                   "'v8-merges@googlegroups.com',",
1291cb0ef41Sopenharmony_ci                                   old_watchlist_content)
1301cb0ef41Sopenharmony_ci    TextToFile(new_watchlist_content, os.path.join(self.default_cwd,
1311cb0ef41Sopenharmony_ci                                                   WATCHLISTS_FILE))
1321cb0ef41Sopenharmony_ci
1331cb0ef41Sopenharmony_ci
1341cb0ef41Sopenharmony_ciclass CommitBranch(Step):
1351cb0ef41Sopenharmony_ci  MESSAGE = "Commit version to new branch."
1361cb0ef41Sopenharmony_ci
1371cb0ef41Sopenharmony_ci  def RunStep(self):
1381cb0ef41Sopenharmony_ci    self["commit_title"] = "Version %s" % self["version"]
1391cb0ef41Sopenharmony_ci    text = "%s" % (self["commit_title"])
1401cb0ef41Sopenharmony_ci    TextToFile(text, self.Config("COMMITMSG_FILE"))
1411cb0ef41Sopenharmony_ci
1421cb0ef41Sopenharmony_ci    self.GitCommit(file_name=self.Config("COMMITMSG_FILE"))
1431cb0ef41Sopenharmony_ci
1441cb0ef41Sopenharmony_ci
1451cb0ef41Sopenharmony_ciclass LandBranch(Step):
1461cb0ef41Sopenharmony_ci  MESSAGE = "Upload and land changes."
1471cb0ef41Sopenharmony_ci
1481cb0ef41Sopenharmony_ci  def RunStep(self):
1491cb0ef41Sopenharmony_ci    if self._options.dry_run:
1501cb0ef41Sopenharmony_ci      print("Dry run - upload CL.")
1511cb0ef41Sopenharmony_ci    else:
1521cb0ef41Sopenharmony_ci      self.GitUpload(force=True,
1531cb0ef41Sopenharmony_ci                     bypass_hooks=True,
1541cb0ef41Sopenharmony_ci                     no_autocc=True,
1551cb0ef41Sopenharmony_ci                     set_bot_commit=True,
1561cb0ef41Sopenharmony_ci                     message_file=self.Config("COMMITMSG_FILE"))
1571cb0ef41Sopenharmony_ci    # TODO(crbug.com/1176141): This might need to go through CQ.
1581cb0ef41Sopenharmony_ci    # We'd need to wait for it to land and then tag it.
1591cb0ef41Sopenharmony_ci    cmd = "cl land --bypass-hooks -f"
1601cb0ef41Sopenharmony_ci    if self._options.dry_run:
1611cb0ef41Sopenharmony_ci      print("Dry run. Command:\ngit %s" % cmd)
1621cb0ef41Sopenharmony_ci    else:
1631cb0ef41Sopenharmony_ci      self.Git(cmd)
1641cb0ef41Sopenharmony_ci
1651cb0ef41Sopenharmony_ci    os.remove(self.Config("COMMITMSG_FILE"))
1661cb0ef41Sopenharmony_ci
1671cb0ef41Sopenharmony_ci
1681cb0ef41Sopenharmony_ciclass TagRevision(Step):
1691cb0ef41Sopenharmony_ci  MESSAGE = "Tag the new revision."
1701cb0ef41Sopenharmony_ci
1711cb0ef41Sopenharmony_ci  def RunStep(self):
1721cb0ef41Sopenharmony_ci    if self._options.dry_run:
1731cb0ef41Sopenharmony_ci      print ("Dry run. Tagging \"%s\" with %s" %
1741cb0ef41Sopenharmony_ci             (self["commit_title"], self["version"]))
1751cb0ef41Sopenharmony_ci    else:
1761cb0ef41Sopenharmony_ci      self.vc.Tag(self["version"],
1771cb0ef41Sopenharmony_ci                  "origin/%s" % self["version"],
1781cb0ef41Sopenharmony_ci                  self["commit_title"])
1791cb0ef41Sopenharmony_ci
1801cb0ef41Sopenharmony_ci
1811cb0ef41Sopenharmony_ciclass CleanUp(Step):
1821cb0ef41Sopenharmony_ci  MESSAGE = "Done!"
1831cb0ef41Sopenharmony_ci
1841cb0ef41Sopenharmony_ci  def RunStep(self):
1851cb0ef41Sopenharmony_ci    print("Congratulations, you have successfully created version %s."
1861cb0ef41Sopenharmony_ci          % self["version"])
1871cb0ef41Sopenharmony_ci
1881cb0ef41Sopenharmony_ci    self.GitCheckout("origin/main")
1891cb0ef41Sopenharmony_ci    self.DeleteBranch("work-branch")
1901cb0ef41Sopenharmony_ci    self.Git("gc")
1911cb0ef41Sopenharmony_ci
1921cb0ef41Sopenharmony_ci
1931cb0ef41Sopenharmony_ciclass CreateRelease(ScriptsBase):
1941cb0ef41Sopenharmony_ci  def _PrepareOptions(self, parser):
1951cb0ef41Sopenharmony_ci    group = parser.add_mutually_exclusive_group()
1961cb0ef41Sopenharmony_ci    group.add_argument("-f", "--force",
1971cb0ef41Sopenharmony_ci                      help="Don't prompt the user.",
1981cb0ef41Sopenharmony_ci                      default=True, action="store_true")
1991cb0ef41Sopenharmony_ci    group.add_argument("-m", "--manual",
2001cb0ef41Sopenharmony_ci                      help="Prompt the user at every important step.",
2011cb0ef41Sopenharmony_ci                      default=False, action="store_true")
2021cb0ef41Sopenharmony_ci    parser.add_argument("-R", "--revision",
2031cb0ef41Sopenharmony_ci                        help="The git commit ID to push (defaults to HEAD).")
2041cb0ef41Sopenharmony_ci
2051cb0ef41Sopenharmony_ci  def _ProcessOptions(self, options):  # pragma: no cover
2061cb0ef41Sopenharmony_ci    if not options.author or not options.reviewer:
2071cb0ef41Sopenharmony_ci      print("Reviewer (-r) and author (-a) are required.")
2081cb0ef41Sopenharmony_ci      return False
2091cb0ef41Sopenharmony_ci    return True
2101cb0ef41Sopenharmony_ci
2111cb0ef41Sopenharmony_ci  def _Config(self):
2121cb0ef41Sopenharmony_ci    return {
2131cb0ef41Sopenharmony_ci      "PERSISTFILE_BASENAME": "/tmp/create-releases-tempfile",
2141cb0ef41Sopenharmony_ci      "COMMITMSG_FILE": "/tmp/v8-create-releases-tempfile-commitmsg",
2151cb0ef41Sopenharmony_ci    }
2161cb0ef41Sopenharmony_ci
2171cb0ef41Sopenharmony_ci  def _Steps(self):
2181cb0ef41Sopenharmony_ci    return [
2191cb0ef41Sopenharmony_ci      Preparation,
2201cb0ef41Sopenharmony_ci      PrepareBranchRevision,
2211cb0ef41Sopenharmony_ci      IncrementVersion,
2221cb0ef41Sopenharmony_ci      DetectLastRelease,
2231cb0ef41Sopenharmony_ci      DeleteBranchRef,
2241cb0ef41Sopenharmony_ci      PushBranchRef,
2251cb0ef41Sopenharmony_ci      MakeBranch,
2261cb0ef41Sopenharmony_ci      SetVersion,
2271cb0ef41Sopenharmony_ci      EnableMergeWatchlist,
2281cb0ef41Sopenharmony_ci      CommitBranch,
2291cb0ef41Sopenharmony_ci      LandBranch,
2301cb0ef41Sopenharmony_ci      TagRevision,
2311cb0ef41Sopenharmony_ci      CleanUp,
2321cb0ef41Sopenharmony_ci    ]
2331cb0ef41Sopenharmony_ci
2341cb0ef41Sopenharmony_ci
2351cb0ef41Sopenharmony_ciif __name__ == "__main__":  # pragma: no cover
2361cb0ef41Sopenharmony_ci  sys.exit(CreateRelease().Run())
237