Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(168)

Side by Side Diff: tools/release/create_release.py

Issue 881683004: Add new script to create release branches. (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: Fix test Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « tools/release/common_includes.py ('k') | tools/release/test_scripts.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 #!/usr/bin/env python
2 # Copyright 2015 the V8 project authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
5
6 import argparse
7 import os
8 import sys
9 import tempfile
10 import urllib2
11
12 from common_includes import *
13
14
15 class Preparation(Step):
16 MESSAGE = "Preparation."
17
18 def RunStep(self):
19 self.Git("fetch origin +refs/heads/*:refs/heads/*")
20 self.Git("fetch origin +refs/branch-heads/*:refs/branch-heads/*")
21 self.Git("fetch origin +refs/pending/*:refs/pending/*")
22 self.Git("fetch origin +refs/pending-tags/*:refs/pending-tags/*")
23 self.GitCheckout("origin/master")
24 self.DeleteBranch("work-branch")
25
26
27 class PrepareBranchRevision(Step):
28 MESSAGE = "Check from which revision to branch off."
29
30 def RunStep(self):
31 if self._options.revision:
32 self["push_hash"], tree_object = self.GitLog(
33 n=1, format="\"%H %T\"", git_hash=self._options.revision).split(" ")
34 else:
35 self["push_hash"], tree_object = self.GitLog(
36 n=1, format="\"%H %T\"", branch="origin/master").split(" ")
37 print "Release revision %s" % self["push_hash"]
38 assert self["push_hash"]
39
40 pending_tuples = self.GitLog(
41 n=200, format="\"%H %T\"", branch="refs/pending/heads/master")
42 for hsh, tree in map(lambda s: s.split(" "), pending_tuples.splitlines()):
43 if tree == tree_object:
44 self["pending_hash"] = hsh
45 break
46 print "Pending release revision %s" % self["pending_hash"]
47 assert self["pending_hash"]
48
49
50 class IncrementVersion(Step):
51 MESSAGE = "Increment version number."
52
53 def RunStep(self):
54 latest_version = self.GetLatestVersion()
55
56 # The version file on master can be used to bump up major/minor at
57 # branch time.
58 self.GitCheckoutFile(VERSION_FILE, self.vc.RemoteMasterBranch())
59 self.ReadAndPersistVersion("master_")
60 master_version = self.ArrayToVersion("master_")
61
62 # Use the highest version from master or from tags to determine the new
63 # version.
64 authoritative_version = sorted(
65 [master_version, latest_version], key=SortingKey)[1]
66 self.StoreVersion(authoritative_version, "authoritative_")
67
68 # Variables prefixed with 'new_' contain the new version numbers for the
69 # ongoing candidates push.
70 self["new_major"] = self["authoritative_major"]
71 self["new_minor"] = self["authoritative_minor"]
72 self["new_build"] = str(int(self["authoritative_build"]) + 1)
73
74 # Make sure patch level is 0 in a new push.
75 self["new_patch"] = "0"
76
77 # The new version is not a candidate.
78 self["new_candidate"] = "0"
79
80 self["version"] = "%s.%s.%s" % (self["new_major"],
81 self["new_minor"],
82 self["new_build"])
83
84 print ("Incremented version to %s" % self["version"])
85
86
87 class DetectLastRelease(Step):
88 MESSAGE = "Detect commit ID of last release base."
89
90 def RunStep(self):
91 self["last_push_master"] = self.GetLatestReleaseBase()
92
93
94 class PrepareChangeLog(Step):
95 MESSAGE = "Prepare raw ChangeLog entry."
96
97 def Reload(self, body):
98 """Attempts to reload the commit message from rietveld in order to allow
99 late changes to the LOG flag. Note: This is brittle to future changes of
100 the web page name or structure.
101 """
102 match = re.search(r"^Review URL: https://codereview\.chromium\.org/(\d+)$",
103 body, flags=re.M)
104 if match:
105 cl_url = ("https://codereview.chromium.org/%s/description"
106 % match.group(1))
107 try:
108 # Fetch from Rietveld but only retry once with one second delay since
109 # there might be many revisions.
110 body = self.ReadURL(cl_url, wait_plan=[1])
111 except urllib2.URLError: # pragma: no cover
112 pass
113 return body
114
115 def RunStep(self):
116 self["date"] = self.GetDate()
117 output = "%s: Version %s\n\n" % (self["date"], self["version"])
118 TextToFile(output, self.Config("CHANGELOG_ENTRY_FILE"))
119 commits = self.GitLog(format="%H",
120 git_hash="%s..%s" % (self["last_push_master"],
121 self["push_hash"]))
122
123 # Cache raw commit messages.
124 commit_messages = [
125 [
126 self.GitLog(n=1, format="%s", git_hash=commit),
127 self.Reload(self.GitLog(n=1, format="%B", git_hash=commit)),
128 self.GitLog(n=1, format="%an", git_hash=commit),
129 ] for commit in commits.splitlines()
130 ]
131
132 # Auto-format commit messages.
133 body = MakeChangeLogBody(commit_messages, auto_format=True)
134 AppendToFile(body, self.Config("CHANGELOG_ENTRY_FILE"))
135
136 msg = (" Performance and stability improvements on all platforms."
137 "\n#\n# The change log above is auto-generated. Please review if "
138 "all relevant\n# commit messages from the list below are included."
139 "\n# All lines starting with # will be stripped.\n#\n")
140 AppendToFile(msg, self.Config("CHANGELOG_ENTRY_FILE"))
141
142 # Include unformatted commit messages as a reference in a comment.
143 comment_body = MakeComment(MakeChangeLogBody(commit_messages))
144 AppendToFile(comment_body, self.Config("CHANGELOG_ENTRY_FILE"))
145
146
147 class EditChangeLog(Step):
148 MESSAGE = "Edit ChangeLog entry."
149
150 def RunStep(self):
151 print ("Please press <Return> to have your EDITOR open the ChangeLog "
152 "entry, then edit its contents to your liking. When you're done, "
153 "save the file and exit your EDITOR. ")
154 self.ReadLine(default="")
155 self.Editor(self.Config("CHANGELOG_ENTRY_FILE"))
156
157 # Strip comments and reformat with correct indentation.
158 changelog_entry = FileToText(self.Config("CHANGELOG_ENTRY_FILE")).rstrip()
159 changelog_entry = StripComments(changelog_entry)
160 changelog_entry = "\n".join(map(Fill80, changelog_entry.splitlines()))
161 changelog_entry = changelog_entry.lstrip()
162
163 if changelog_entry == "": # pragma: no cover
164 self.Die("Empty ChangeLog entry.")
165
166 # Safe new change log for adding it later to the candidates patch.
167 TextToFile(changelog_entry, self.Config("CHANGELOG_ENTRY_FILE"))
168
169
170 class MakeBranch(Step):
171 MESSAGE = "Create the branch."
172
173 def RunStep(self):
174 self.Git("reset --hard origin/master")
175 self.Git("checkout -b work-branch %s" % self["pending_hash"])
176 self.GitCheckoutFile(CHANGELOG_FILE, self["latest_version"])
177 self.GitCheckoutFile(VERSION_FILE, self["latest_version"])
178
179
180 class AddChangeLog(Step):
181 MESSAGE = "Add ChangeLog changes to release branch."
182
183 def RunStep(self):
184 changelog_entry = FileToText(self.Config("CHANGELOG_ENTRY_FILE"))
185 old_change_log = FileToText(os.path.join(self.default_cwd, CHANGELOG_FILE))
186 new_change_log = "%s\n\n\n%s" % (changelog_entry, old_change_log)
187 TextToFile(new_change_log, os.path.join(self.default_cwd, CHANGELOG_FILE))
188
189
190 class SetVersion(Step):
191 MESSAGE = "Set correct version for candidates."
192
193 def RunStep(self):
194 self.SetVersion(os.path.join(self.default_cwd, VERSION_FILE), "new_")
195
196
197 class CommitBranch(Step):
198 MESSAGE = "Commit version and changelog to new branch."
199
200 def RunStep(self):
201 # Convert the ChangeLog entry to commit message format.
202 text = FileToText(self.Config("CHANGELOG_ENTRY_FILE"))
203
204 # Remove date and trailing white space.
205 text = re.sub(r"^%s: " % self["date"], "", text.rstrip())
206
207 # Remove indentation and merge paragraphs into single long lines, keeping
208 # empty lines between them.
209 def SplitMapJoin(split_text, fun, join_text):
210 return lambda text: join_text.join(map(fun, text.split(split_text)))
211 text = SplitMapJoin(
212 "\n\n", SplitMapJoin("\n", str.strip, " "), "\n\n")(text)
213
214 if not text: # pragma: no cover
215 self.Die("Commit message editing failed.")
216 self["commit_title"] = text.splitlines()[0]
217 TextToFile(text, self.Config("COMMITMSG_FILE"))
218
219 self.GitCommit(file_name = self.Config("COMMITMSG_FILE"))
220 os.remove(self.Config("COMMITMSG_FILE"))
221 os.remove(self.Config("CHANGELOG_ENTRY_FILE"))
222
223
224 class PushBranch(Step):
225 MESSAGE = "Push changes."
226
227 def RunStep(self):
228 pushspecs = [
229 "refs/heads/work-branch:refs/pending/branch-heads/%s" % self["version"],
230 "%s:refs/pending-tags/branch-heads/%s" %
231 (self["pending_hash"], self["version"]),
232 "%s:refs/branch-heads/%s" % (self["push_hash"], self["version"]),
233 ]
234 cmd = "push origin %s" % " ".join(pushspecs)
235 if self._options.dry_run:
236 print "Dry run. Command:\ngit %s" % cmd
237 else:
238 self.Git(cmd)
239
240
241 class TagRevision(Step):
242 MESSAGE = "Tag the new revision."
243
244 def RunStep(self):
245 if self._options.dry_run:
246 print ("Dry run. Tagging \"%s\" with %s" %
247 (self["commit_title"], self["version"]))
248 else:
249 self.vc.Tag(self["version"],
250 "branch-heads/%s" % self["version"],
251 self["commit_title"])
252
253
254 class CleanUp(Step):
255 MESSAGE = "Done!"
256
257 def RunStep(self):
258 print("Congratulations, you have successfully created version %s."
259 % self["version"])
260
261 self.GitCheckout("origin/master")
262 self.DeleteBranch("work-branch")
263 self.Git("gc")
264
265
266 class CreateRelease(ScriptsBase):
267 def _PrepareOptions(self, parser):
268 group = parser.add_mutually_exclusive_group()
269 group.add_argument("-f", "--force",
270 help="Don't prompt the user.",
271 default=True, action="store_true")
272 group.add_argument("-m", "--manual",
273 help="Prompt the user at every important step.",
274 default=False, action="store_true")
275 parser.add_argument("-R", "--revision",
276 help="The git commit ID to push (defaults to HEAD).")
277
278 def _ProcessOptions(self, options): # pragma: no cover
279 if not options.author or not options.reviewer:
280 print "Reviewer (-r) and author (-a) are required."
281 return False
282 return True
283
284 def _Config(self):
285 return {
286 "PERSISTFILE_BASENAME": "/tmp/create-releases-tempfile",
287 "CHANGELOG_ENTRY_FILE":
288 "/tmp/v8-create-releases-tempfile-changelog-entry",
289 "COMMITMSG_FILE": "/tmp/v8-create-releases-tempfile-commitmsg",
290 }
291
292 def _Steps(self):
293 return [
294 Preparation,
295 PrepareBranchRevision,
296 IncrementVersion,
297 DetectLastRelease,
298 PrepareChangeLog,
299 EditChangeLog,
300 MakeBranch,
301 AddChangeLog,
302 SetVersion,
303 CommitBranch,
304 PushBranch,
305 TagRevision,
306 CleanUp,
307 ]
308
309
310 if __name__ == "__main__": # pragma: no cover
311 sys.exit(CreateRelease().Run())
OLDNEW
« no previous file with comments | « tools/release/common_includes.py ('k') | tools/release/test_scripts.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698