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

Side by Side Diff: tools/push-to-trunk/push_to_trunk.py

Issue 868473004: External name changes of release scripts. (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: Created 5 years, 11 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
OLDNEW
(Empty)
1 #!/usr/bin/env python
2 # Copyright 2013 the V8 project authors. All rights reserved.
3 # Redistribution and use in source and binary forms, with or without
4 # modification, are permitted provided that the following conditions are
5 # met:
6 #
7 # * Redistributions of source code must retain the above copyright
8 # notice, this list of conditions and the following disclaimer.
9 # * Redistributions in binary form must reproduce the above
10 # copyright notice, this list of conditions and the following
11 # disclaimer in the documentation and/or other materials provided
12 # with the distribution.
13 # * Neither the name of Google Inc. nor the names of its
14 # contributors may be used to endorse or promote products derived
15 # from this software without specific prior written permission.
16 #
17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29 import argparse
30 import os
31 import sys
32 import tempfile
33 import urllib2
34
35 from common_includes import *
36
37 PUSH_MSG_GIT_SUFFIX = " (based on %s)"
38 PUSH_MSG_GIT_RE = re.compile(r".* \(based on (?P<git_rev>[a-fA-F0-9]+)\)$")
39 VERSION_RE = re.compile(r"^\d+\.\d+\.\d+(?:\.\d+)?$")
40
41 class Preparation(Step):
42 MESSAGE = "Preparation."
43
44 def RunStep(self):
45 self.InitialEnvironmentChecks(self.default_cwd)
46 self.CommonPrepare()
47
48 # Make sure tags are fetched.
49 self.Git("fetch origin +refs/tags/*:refs/tags/*")
50
51 if(self["current_branch"] == self.Config("CANDIDATESBRANCH")
52 or self["current_branch"] == self.Config("BRANCHNAME")):
53 print "Warning: Script started on branch %s" % self["current_branch"]
54
55 self.PrepareBranch()
56 self.DeleteBranch(self.Config("CANDIDATESBRANCH"))
57
58
59 class FreshBranch(Step):
60 MESSAGE = "Create a fresh branch."
61
62 def RunStep(self):
63 self.GitCreateBranch(self.Config("BRANCHNAME"),
64 self.vc.RemoteMasterBranch())
65
66
67 class PreparePushRevision(Step):
68 MESSAGE = "Check which revision to push."
69
70 def RunStep(self):
71 if self._options.revision:
72 self["push_hash"] = self._options.revision
73 else:
74 self["push_hash"] = self.GitLog(n=1, format="%H", git_hash="HEAD")
75 if not self["push_hash"]: # pragma: no cover
76 self.Die("Could not determine the git hash for the push.")
77
78
79 class DetectLastPush(Step):
80 MESSAGE = "Detect commit ID of last push to CANDIDATES."
81
82 def RunStep(self):
83 last_push = self._options.last_push or self.FindLastCandidatesPush()
84 while True:
85 # Print assumed commit, circumventing git's pager.
86 print self.GitLog(n=1, git_hash=last_push)
87 if self.Confirm(
88 "Is the commit printed above the last push to candidates?"):
89 break
90 last_push = self.FindLastCandidatesPush(parent_hash=last_push)
91
92 if self._options.last_master:
93 # Read the master revision of the last push from a command-line option.
94 last_push_master = self._options.last_master
95 else:
96 # Retrieve the master revision of the last push from the text in
97 # the push commit message.
98 last_push_title = self.GitLog(n=1, format="%s", git_hash=last_push)
99 last_push_master = PUSH_MSG_GIT_RE.match(
100 last_push_title).group("git_rev")
101
102 if not last_push_master: # pragma: no cover
103 self.Die(
104 "Could not retrieve master git hash for candidates push %s"
105 % last_push)
106
107 # This points to the git hash of the last push on candidates.
108 self["last_push_candidates"] = last_push
109 # This points to the last master revision that went into the last
110 # push.
111 # TODO(machenbach): Do we need a check to make sure we're not pushing a
112 # revision older than the last push? If we do this, the output of the
113 # current change log preparation won't make much sense.
114 self["last_push_master"] = last_push_master
115
116
117 class GetLatestVersion(Step):
118 MESSAGE = "Get latest version from tags."
119
120 def RunStep(self):
121 versions = sorted(filter(VERSION_RE.match, self.vc.GetTags()),
122 key=SortingKey, reverse=True)
123 self.StoreVersion(versions[0], "latest_")
124 self["latest_version"] = self.ArrayToVersion("latest_")
125
126 # The version file on master can be used to bump up major/minor at
127 # branch time.
128 self.GitCheckoutFile(VERSION_FILE, self.vc.RemoteMasterBranch())
129 self.ReadAndPersistVersion("master_")
130 self["master_version"] = self.ArrayToVersion("master_")
131
132 if SortingKey(self["master_version"]) > SortingKey(self["latest_version"]):
133 self["latest_version"] = self["master_version"]
134 self.StoreVersion(self["latest_version"], "latest_")
135
136 print "Determined latest version %s" % self["latest_version"]
137
138
139 class IncrementVersion(Step):
140 MESSAGE = "Increment version number."
141
142 def RunStep(self):
143 # Variables prefixed with 'new_' contain the new version numbers for the
144 # ongoing candidates push.
145 self["new_major"] = self["latest_major"]
146 self["new_minor"] = self["latest_minor"]
147 self["new_build"] = str(int(self["latest_build"]) + 1)
148
149 # Make sure patch level is 0 in a new push.
150 self["new_patch"] = "0"
151
152 self["version"] = "%s.%s.%s" % (self["new_major"],
153 self["new_minor"],
154 self["new_build"])
155
156
157 class PrepareChangeLog(Step):
158 MESSAGE = "Prepare raw ChangeLog entry."
159
160 def Reload(self, body):
161 """Attempts to reload the commit message from rietveld in order to allow
162 late changes to the LOG flag. Note: This is brittle to future changes of
163 the web page name or structure.
164 """
165 match = re.search(r"^Review URL: https://codereview\.chromium\.org/(\d+)$",
166 body, flags=re.M)
167 if match:
168 cl_url = ("https://codereview.chromium.org/%s/description"
169 % match.group(1))
170 try:
171 # Fetch from Rietveld but only retry once with one second delay since
172 # there might be many revisions.
173 body = self.ReadURL(cl_url, wait_plan=[1])
174 except urllib2.URLError: # pragma: no cover
175 pass
176 return body
177
178 def RunStep(self):
179 self["date"] = self.GetDate()
180 output = "%s: Version %s\n\n" % (self["date"], self["version"])
181 TextToFile(output, self.Config("CHANGELOG_ENTRY_FILE"))
182 commits = self.GitLog(format="%H",
183 git_hash="%s..%s" % (self["last_push_master"],
184 self["push_hash"]))
185
186 # Cache raw commit messages.
187 commit_messages = [
188 [
189 self.GitLog(n=1, format="%s", git_hash=commit),
190 self.Reload(self.GitLog(n=1, format="%B", git_hash=commit)),
191 self.GitLog(n=1, format="%an", git_hash=commit),
192 ] for commit in commits.splitlines()
193 ]
194
195 # Auto-format commit messages.
196 body = MakeChangeLogBody(commit_messages, auto_format=True)
197 AppendToFile(body, self.Config("CHANGELOG_ENTRY_FILE"))
198
199 msg = (" Performance and stability improvements on all platforms."
200 "\n#\n# The change log above is auto-generated. Please review if "
201 "all relevant\n# commit messages from the list below are included."
202 "\n# All lines starting with # will be stripped.\n#\n")
203 AppendToFile(msg, self.Config("CHANGELOG_ENTRY_FILE"))
204
205 # Include unformatted commit messages as a reference in a comment.
206 comment_body = MakeComment(MakeChangeLogBody(commit_messages))
207 AppendToFile(comment_body, self.Config("CHANGELOG_ENTRY_FILE"))
208
209
210 class EditChangeLog(Step):
211 MESSAGE = "Edit ChangeLog entry."
212
213 def RunStep(self):
214 print ("Please press <Return> to have your EDITOR open the ChangeLog "
215 "entry, then edit its contents to your liking. When you're done, "
216 "save the file and exit your EDITOR. ")
217 self.ReadLine(default="")
218 self.Editor(self.Config("CHANGELOG_ENTRY_FILE"))
219
220 # Strip comments and reformat with correct indentation.
221 changelog_entry = FileToText(self.Config("CHANGELOG_ENTRY_FILE")).rstrip()
222 changelog_entry = StripComments(changelog_entry)
223 changelog_entry = "\n".join(map(Fill80, changelog_entry.splitlines()))
224 changelog_entry = changelog_entry.lstrip()
225
226 if changelog_entry == "": # pragma: no cover
227 self.Die("Empty ChangeLog entry.")
228
229 # Safe new change log for adding it later to the candidates patch.
230 TextToFile(changelog_entry, self.Config("CHANGELOG_ENTRY_FILE"))
231
232
233 class StragglerCommits(Step):
234 MESSAGE = ("Fetch straggler commits that sneaked in since this script was "
235 "started.")
236
237 def RunStep(self):
238 self.vc.Fetch()
239 self.GitCheckout(self.vc.RemoteMasterBranch())
240
241
242 class SquashCommits(Step):
243 MESSAGE = "Squash commits into one."
244
245 def RunStep(self):
246 # Instead of relying on "git rebase -i", we'll just create a diff, because
247 # that's easier to automate.
248 TextToFile(self.GitDiff(self.vc.RemoteCandidateBranch(),
249 self["push_hash"]),
250 self.Config("PATCH_FILE"))
251
252 # Convert the ChangeLog entry to commit message format.
253 text = FileToText(self.Config("CHANGELOG_ENTRY_FILE"))
254
255 # Remove date and trailing white space.
256 text = re.sub(r"^%s: " % self["date"], "", text.rstrip())
257
258 # Show the used master hash in the commit message.
259 suffix = PUSH_MSG_GIT_SUFFIX % self["push_hash"]
260 text = MSub(r"^(Version \d+\.\d+\.\d+)$", "\\1%s" % suffix, text)
261
262 # Remove indentation and merge paragraphs into single long lines, keeping
263 # empty lines between them.
264 def SplitMapJoin(split_text, fun, join_text):
265 return lambda text: join_text.join(map(fun, text.split(split_text)))
266 strip = lambda line: line.strip()
267 text = SplitMapJoin("\n\n", SplitMapJoin("\n", strip, " "), "\n\n")(text)
268
269 if not text: # pragma: no cover
270 self.Die("Commit message editing failed.")
271 self["commit_title"] = text.splitlines()[0]
272 TextToFile(text, self.Config("COMMITMSG_FILE"))
273
274
275 class NewBranch(Step):
276 MESSAGE = "Create a new branch from candidates."
277
278 def RunStep(self):
279 self.GitCreateBranch(self.Config("CANDIDATESBRANCH"),
280 self.vc.RemoteCandidateBranch())
281
282
283 class ApplyChanges(Step):
284 MESSAGE = "Apply squashed changes."
285
286 def RunStep(self):
287 self.ApplyPatch(self.Config("PATCH_FILE"))
288 os.remove(self.Config("PATCH_FILE"))
289 # The change log has been modified by the patch. Reset it to the version
290 # on candidates and apply the exact changes determined by this
291 # PrepareChangeLog step above.
292 self.GitCheckoutFile(CHANGELOG_FILE, self.vc.RemoteCandidateBranch())
293 # The version file has been modified by the patch. Reset it to the version
294 # on candidates.
295 self.GitCheckoutFile(VERSION_FILE, self.vc.RemoteCandidateBranch())
296
297
298 class CommitSquash(Step):
299 MESSAGE = "Commit to local candidates branch."
300
301 def RunStep(self):
302 # Make a first commit with a slightly different title to not confuse
303 # the tagging.
304 msg = FileToText(self.Config("COMMITMSG_FILE")).splitlines()
305 msg[0] = msg[0].replace("(based on", "(squashed - based on")
306 self.GitCommit(message = "\n".join(msg))
307
308
309 class PrepareVersionBranch(Step):
310 MESSAGE = "Prepare new branch to commit version and changelog file."
311
312 def RunStep(self):
313 self.GitCheckout("master")
314 self.Git("fetch")
315 self.GitDeleteBranch(self.Config("CANDIDATESBRANCH"))
316 self.GitCreateBranch(self.Config("CANDIDATESBRANCH"),
317 self.vc.RemoteCandidateBranch())
318
319
320 class AddChangeLog(Step):
321 MESSAGE = "Add ChangeLog changes to candidates branch."
322
323 def RunStep(self):
324 changelog_entry = FileToText(self.Config("CHANGELOG_ENTRY_FILE"))
325 old_change_log = FileToText(os.path.join(self.default_cwd, CHANGELOG_FILE))
326 new_change_log = "%s\n\n\n%s" % (changelog_entry, old_change_log)
327 TextToFile(new_change_log, os.path.join(self.default_cwd, CHANGELOG_FILE))
328 os.remove(self.Config("CHANGELOG_ENTRY_FILE"))
329
330
331 class SetVersion(Step):
332 MESSAGE = "Set correct version for candidates."
333
334 def RunStep(self):
335 self.SetVersion(os.path.join(self.default_cwd, VERSION_FILE), "new_")
336
337
338 class CommitCandidate(Step):
339 MESSAGE = "Commit version and changelog to local candidates branch."
340
341 def RunStep(self):
342 self.GitCommit(file_name = self.Config("COMMITMSG_FILE"))
343 os.remove(self.Config("COMMITMSG_FILE"))
344
345
346 class SanityCheck(Step):
347 MESSAGE = "Sanity check."
348
349 def RunStep(self):
350 # TODO(machenbach): Run presubmit script here as it is now missing in the
351 # prepare push process.
352 if not self.Confirm("Please check if your local checkout is sane: Inspect "
353 "%s, compile, run tests. Do you want to commit this new candidates "
354 "revision to the repository?" % VERSION_FILE):
355 self.Die("Execution canceled.") # pragma: no cover
356
357
358 class Land(Step):
359 MESSAGE = "Land the patch."
360
361 def RunStep(self):
362 self.vc.CLLand()
363
364
365 class TagRevision(Step):
366 MESSAGE = "Tag the new revision."
367
368 def RunStep(self):
369 self.vc.Tag(
370 self["version"], self.vc.RemoteCandidateBranch(), self["commit_title"])
371
372
373 class CleanUp(Step):
374 MESSAGE = "Done!"
375
376 def RunStep(self):
377 print("Congratulations, you have successfully created the candidates "
378 "revision %s."
379 % self["version"])
380
381 self.CommonCleanup()
382 if self.Config("CANDIDATESBRANCH") != self["current_branch"]:
383 self.GitDeleteBranch(self.Config("CANDIDATESBRANCH"))
384
385
386 class PushToCandidates(ScriptsBase):
387 def _PrepareOptions(self, parser):
388 group = parser.add_mutually_exclusive_group()
389 group.add_argument("-f", "--force",
390 help="Don't prompt the user.",
391 default=False, action="store_true")
392 group.add_argument("-m", "--manual",
393 help="Prompt the user at every important step.",
394 default=False, action="store_true")
395 parser.add_argument("-b", "--last-master",
396 help=("The git commit ID of the last master "
397 "revision that was pushed to candidates. This is"
398 " used for the auto-generated ChangeLog entry."))
399 parser.add_argument("-l", "--last-push",
400 help="The git commit ID of the last candidates push.")
401 parser.add_argument("-R", "--revision",
402 help="The git commit ID to push (defaults to HEAD).")
403
404 def _ProcessOptions(self, options): # pragma: no cover
405 if not options.manual and not options.reviewer:
406 print "A reviewer (-r) is required in (semi-)automatic mode."
407 return False
408 if not options.manual and not options.author:
409 print "Specify your chromium.org email with -a in (semi-)automatic mode."
410 return False
411
412 options.tbr_commit = not options.manual
413 return True
414
415 def _Config(self):
416 return {
417 "BRANCHNAME": "prepare-push",
418 "CANDIDATESBRANCH": "candidates-push",
419 "PERSISTFILE_BASENAME": "/tmp/v8-push-to-candidates-tempfile",
420 "CHANGELOG_ENTRY_FILE":
421 "/tmp/v8-push-to-candidates-tempfile-changelog-entry",
422 "PATCH_FILE": "/tmp/v8-push-to-candidates-tempfile-patch-file",
423 "COMMITMSG_FILE": "/tmp/v8-push-to-candidates-tempfile-commitmsg",
424 }
425
426 def _Steps(self):
427 return [
428 Preparation,
429 FreshBranch,
430 PreparePushRevision,
431 DetectLastPush,
432 GetLatestVersion,
433 IncrementVersion,
434 PrepareChangeLog,
435 EditChangeLog,
436 StragglerCommits,
437 SquashCommits,
438 NewBranch,
439 ApplyChanges,
440 CommitSquash,
441 SanityCheck,
442 Land,
443 PrepareVersionBranch,
444 AddChangeLog,
445 SetVersion,
446 CommitCandidate,
447 Land,
448 TagRevision,
449 CleanUp,
450 ]
451
452
453 if __name__ == "__main__": # pragma: no cover
454 sys.exit(PushToCandidates().Run())
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698