v8/tools/release/create_release.py
Michael Achenbach a3496f4afb [release] Force branch commit messages on upload
Gerrit seems to concatenate several commit messages on uploading branch
creation CLs. Now we pass the commit message of the branch commit to
the upload script to prevent this.

NOTRY=true

Bug: v8:8546
Change-Id: Ia4673261aad2f40bcda4384889a0428039adae74
Reviewed-on: https://chromium-review.googlesource.com/c/1367454
Reviewed-by: Sergiy Belozorov <sergiyb@chromium.org>
Commit-Queue: Michael Achenbach <machenbach@chromium.org>
Cr-Commit-Position: refs/heads/master@{#58092}
2018-12-07 13:19:02 +00:00

307 lines
9.9 KiB
Python
Executable File

#!/usr/bin/env python
# Copyright 2015 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 os
import sys
import tempfile
import urllib2
from common_includes import *
class Preparation(Step):
MESSAGE = "Preparation."
def RunStep(self):
self.Git("fetch origin +refs/heads/*:refs/heads/*")
self.GitCheckout("origin/master")
self.DeleteBranch("work-branch")
class PrepareBranchRevision(Step):
MESSAGE = "Check from which revision to branch off."
def RunStep(self):
self["push_hash"] = (self._options.revision or
self.GitLog(n=1, format="%H", branch="origin/master"))
assert self["push_hash"]
print "Release revision %s" % self["push_hash"]
class IncrementVersion(Step):
MESSAGE = "Increment version number."
def RunStep(self):
latest_version = self.GetLatestVersion()
# The version file on master can be used to bump up major/minor at
# branch time.
self.GitCheckoutFile(VERSION_FILE, self.vc.RemoteMasterBranch())
self.ReadAndPersistVersion("master_")
master_version = self.ArrayToVersion("master_")
# Use the highest version from master or from tags to determine the new
# version.
authoritative_version = sorted(
[master_version, latest_version], key=SortingKey)[1]
self.StoreVersion(authoritative_version, "authoritative_")
# Variables prefixed with 'new_' contain the new version numbers for the
# ongoing candidates push.
self["new_major"] = self["authoritative_major"]
self["new_minor"] = self["authoritative_minor"]
self["new_build"] = str(int(self["authoritative_build"]) + 1)
# Make sure patch level is 0 in a new push.
self["new_patch"] = "0"
# The new version is not a candidate.
self["new_candidate"] = "0"
self["version"] = "%s.%s.%s" % (self["new_major"],
self["new_minor"],
self["new_build"])
print ("Incremented version to %s" % self["version"])
class DetectLastRelease(Step):
MESSAGE = "Detect commit ID of last release base."
def RunStep(self):
self["last_push_master"] = self.GetLatestReleaseBase()
class PrepareChangeLog(Step):
MESSAGE = "Prepare raw ChangeLog entry."
def RunStep(self):
self["date"] = self.GetDate()
output = "%s: Version %s\n\n" % (self["date"], self["version"])
TextToFile(output, self.Config("CHANGELOG_ENTRY_FILE"))
commits = self.GitLog(format="%H",
git_hash="%s..%s" % (self["last_push_master"],
self["push_hash"]))
# Cache raw commit messages.
commit_messages = [
[
self.GitLog(n=1, format="%s", git_hash=commit),
self.GitLog(n=1, format="%B", git_hash=commit),
self.GitLog(n=1, format="%an", git_hash=commit),
] for commit in commits.splitlines()
]
# Auto-format commit messages.
body = MakeChangeLogBody(commit_messages, auto_format=True)
AppendToFile(body, self.Config("CHANGELOG_ENTRY_FILE"))
msg = (" Performance and stability improvements on all platforms."
"\n#\n# The change log above is auto-generated. Please review if "
"all relevant\n# commit messages from the list below are included."
"\n# All lines starting with # will be stripped.\n#\n")
AppendToFile(msg, self.Config("CHANGELOG_ENTRY_FILE"))
# Include unformatted commit messages as a reference in a comment.
comment_body = MakeComment(MakeChangeLogBody(commit_messages))
AppendToFile(comment_body, self.Config("CHANGELOG_ENTRY_FILE"))
class EditChangeLog(Step):
MESSAGE = "Edit ChangeLog entry."
def RunStep(self):
print ("Please press <Return> to have your EDITOR open the ChangeLog "
"entry, then edit its contents to your liking. When you're done, "
"save the file and exit your EDITOR. ")
self.ReadLine(default="")
self.Editor(self.Config("CHANGELOG_ENTRY_FILE"))
# Strip comments and reformat with correct indentation.
changelog_entry = FileToText(self.Config("CHANGELOG_ENTRY_FILE")).rstrip()
changelog_entry = StripComments(changelog_entry)
changelog_entry = "\n".join(map(Fill80, changelog_entry.splitlines()))
changelog_entry = changelog_entry.lstrip()
if changelog_entry == "": # pragma: no cover
self.Die("Empty ChangeLog entry.")
# Safe new change log for adding it later to the candidates patch.
TextToFile(changelog_entry, self.Config("CHANGELOG_ENTRY_FILE"))
class PushBranchRef(Step):
MESSAGE = "Create branch ref."
def RunStep(self):
cmd = "push origin %s:refs/heads/%s" % (self["push_hash"], self["version"])
if self._options.dry_run:
print "Dry run. Command:\ngit %s" % cmd
else:
self.Git(cmd)
class MakeBranch(Step):
MESSAGE = "Create the branch."
def RunStep(self):
self.Git("reset --hard origin/master")
self.Git("new-branch work-branch --upstream origin/%s" % self["version"])
self.GitCheckoutFile(CHANGELOG_FILE, self["latest_version"])
self.GitCheckoutFile(VERSION_FILE, self["latest_version"])
self.GitCheckoutFile(WATCHLISTS_FILE, self["latest_version"])
class AddChangeLog(Step):
MESSAGE = "Add ChangeLog changes to release branch."
def RunStep(self):
changelog_entry = FileToText(self.Config("CHANGELOG_ENTRY_FILE"))
old_change_log = FileToText(os.path.join(self.default_cwd, CHANGELOG_FILE))
new_change_log = "%s\n\n\n%s" % (changelog_entry, old_change_log)
TextToFile(new_change_log, os.path.join(self.default_cwd, CHANGELOG_FILE))
class SetVersion(Step):
MESSAGE = "Set correct version for candidates."
def RunStep(self):
self.SetVersion(os.path.join(self.default_cwd, VERSION_FILE), "new_")
class EnableMergeWatchlist(Step):
MESSAGE = "Enable watchlist entry for merge notifications."
def RunStep(self):
old_watchlist_content = FileToText(os.path.join(self.default_cwd,
WATCHLISTS_FILE))
new_watchlist_content = re.sub("(# 'v8-merges@googlegroups\.com',)",
"'v8-merges@googlegroups.com',",
old_watchlist_content)
TextToFile(new_watchlist_content, os.path.join(self.default_cwd,
WATCHLISTS_FILE))
class CommitBranch(Step):
MESSAGE = "Commit version and changelog to new branch."
def RunStep(self):
# Convert the ChangeLog entry to commit message format.
text = FileToText(self.Config("CHANGELOG_ENTRY_FILE"))
# Remove date and trailing white space.
text = re.sub(r"^%s: " % self["date"], "", text.rstrip())
# Remove indentation and merge paragraphs into single long lines, keeping
# empty lines between them.
def SplitMapJoin(split_text, fun, join_text):
return lambda text: join_text.join(map(fun, text.split(split_text)))
text = SplitMapJoin(
"\n\n", SplitMapJoin("\n", str.strip, " "), "\n\n")(text)
if not text: # pragma: no cover
self.Die("Commit message editing failed.")
text += "\n\nTBR=%s" % self._options.reviewer
self["commit_title"] = text.splitlines()[0]
TextToFile(text, self.Config("COMMITMSG_FILE"))
self.GitCommit(file_name=self.Config("COMMITMSG_FILE"))
os.remove(self.Config("CHANGELOG_ENTRY_FILE"))
class LandBranch(Step):
MESSAGE = "Upload and land changes."
def RunStep(self):
if self._options.dry_run:
print "Dry run - upload CL."
else:
self.GitUpload(force=True,
bypass_hooks=True,
no_autocc=True,
message_file=self.Config("COMMITMSG_FILE"))
cmd = "cl land --bypass-hooks -f"
if self._options.dry_run:
print "Dry run. Command:\ngit %s" % cmd
else:
self.Git(cmd)
os.remove(self.Config("COMMITMSG_FILE"))
class TagRevision(Step):
MESSAGE = "Tag the new revision."
def RunStep(self):
if self._options.dry_run:
print ("Dry run. Tagging \"%s\" with %s" %
(self["commit_title"], self["version"]))
else:
self.vc.Tag(self["version"],
"origin/%s" % self["version"],
self["commit_title"])
class CleanUp(Step):
MESSAGE = "Done!"
def RunStep(self):
print("Congratulations, you have successfully created version %s."
% self["version"])
self.GitCheckout("origin/master")
self.DeleteBranch("work-branch")
self.Git("gc")
class CreateRelease(ScriptsBase):
def _PrepareOptions(self, parser):
group = parser.add_mutually_exclusive_group()
group.add_argument("-f", "--force",
help="Don't prompt the user.",
default=True, action="store_true")
group.add_argument("-m", "--manual",
help="Prompt the user at every important step.",
default=False, action="store_true")
parser.add_argument("-R", "--revision",
help="The git commit ID to push (defaults to HEAD).")
def _ProcessOptions(self, options): # pragma: no cover
if not options.author or not options.reviewer:
print "Reviewer (-r) and author (-a) are required."
return False
return True
def _Config(self):
return {
"PERSISTFILE_BASENAME": "/tmp/create-releases-tempfile",
"CHANGELOG_ENTRY_FILE":
"/tmp/v8-create-releases-tempfile-changelog-entry",
"COMMITMSG_FILE": "/tmp/v8-create-releases-tempfile-commitmsg",
}
def _Steps(self):
return [
Preparation,
PrepareBranchRevision,
IncrementVersion,
DetectLastRelease,
PrepareChangeLog,
EditChangeLog,
PushBranchRef,
MakeBranch,
AddChangeLog,
SetVersion,
EnableMergeWatchlist,
CommitBranch,
LandBranch,
TagRevision,
CleanUp,
]
if __name__ == "__main__": # pragma: no cover
sys.exit(CreateRelease().Run())