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

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

Issue 185653004: Experimental parser: merge to r19637 (Closed) Base URL: https://v8.googlecode.com/svn/branches/experimental/parser
Patch Set: Created 6 years, 9 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 | Annotate | Revision Log
« no previous file with comments | « tools/push-to-trunk/merge_to_branch.py ('k') | tools/push-to-trunk/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
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # Copyright 2013 the V8 project authors. All rights reserved. 2 # Copyright 2013 the V8 project authors. All rights reserved.
3 # Redistribution and use in source and binary forms, with or without 3 # Redistribution and use in source and binary forms, with or without
4 # modification, are permitted provided that the following conditions are 4 # modification, are permitted provided that the following conditions are
5 # met: 5 # met:
6 # 6 #
7 # * Redistributions of source code must retain the above copyright 7 # * Redistributions of source code must retain the above copyright
8 # notice, this list of conditions and the following disclaimer. 8 # notice, this list of conditions and the following disclaimer.
9 # * Redistributions in binary form must reproduce the above 9 # * Redistributions in binary form must reproduce the above
10 # copyright notice, this list of conditions and the following 10 # copyright notice, this list of conditions and the following
11 # disclaimer in the documentation and/or other materials provided 11 # disclaimer in the documentation and/or other materials provided
12 # with the distribution. 12 # with the distribution.
13 # * Neither the name of Google Inc. nor the names of its 13 # * Neither the name of Google Inc. nor the names of its
14 # contributors may be used to endorse or promote products derived 14 # contributors may be used to endorse or promote products derived
15 # from this software without specific prior written permission. 15 # from this software without specific prior written permission.
16 # 16 #
17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 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. 27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 28
29 import optparse 29 import argparse
30 import sys 30 import sys
31 import tempfile 31 import tempfile
32 import urllib2 32 import urllib2
33 33
34 from common_includes import * 34 from common_includes import *
35 35
36 TRUNKBRANCH = "TRUNKBRANCH" 36 TRUNKBRANCH = "TRUNKBRANCH"
37 CHROMIUM = "CHROMIUM" 37 CHROMIUM = "CHROMIUM"
38 DEPS_FILE = "DEPS_FILE" 38 DEPS_FILE = "DEPS_FILE"
39 39
40 CONFIG = { 40 CONFIG = {
41 BRANCHNAME: "prepare-push", 41 BRANCHNAME: "prepare-push",
42 TRUNKBRANCH: "trunk-push", 42 TRUNKBRANCH: "trunk-push",
43 PERSISTFILE_BASENAME: "/tmp/v8-push-to-trunk-tempfile", 43 PERSISTFILE_BASENAME: "/tmp/v8-push-to-trunk-tempfile",
44 TEMP_BRANCH: "prepare-push-temporary-branch-created-by-script", 44 TEMP_BRANCH: "prepare-push-temporary-branch-created-by-script",
45 DOT_GIT_LOCATION: ".git", 45 DOT_GIT_LOCATION: ".git",
46 VERSION_FILE: "src/version.cc", 46 VERSION_FILE: "src/version.cc",
47 CHANGELOG_FILE: "ChangeLog", 47 CHANGELOG_FILE: "ChangeLog",
48 CHANGELOG_ENTRY_FILE: "/tmp/v8-push-to-trunk-tempfile-changelog-entry", 48 CHANGELOG_ENTRY_FILE: "/tmp/v8-push-to-trunk-tempfile-changelog-entry",
49 PATCH_FILE: "/tmp/v8-push-to-trunk-tempfile-patch-file", 49 PATCH_FILE: "/tmp/v8-push-to-trunk-tempfile-patch-file",
50 COMMITMSG_FILE: "/tmp/v8-push-to-trunk-tempfile-commitmsg", 50 COMMITMSG_FILE: "/tmp/v8-push-to-trunk-tempfile-commitmsg",
51 DEPS_FILE: "DEPS", 51 DEPS_FILE: "DEPS",
52 } 52 }
53 53
54 PUSH_MESSAGE_SUFFIX = " (based on bleeding_edge revision r%d)"
55 PUSH_MESSAGE_RE = re.compile(r".* \(based on bleeding_edge revision r(\d+)\)$")
56
54 57
55 class PushToTrunkOptions(CommonOptions): 58 class PushToTrunkOptions(CommonOptions):
56 @staticmethod 59 @staticmethod
57 def MakeForcedOptions(reviewer, chrome_path): 60 def MakeForcedOptions(author, reviewer, chrome_path):
58 """Convenience wrapper.""" 61 """Convenience wrapper."""
59 class Options(object): 62 class Options(object):
60 pass 63 pass
61 options = Options() 64 options = Options()
62 options.s = 0 65 options.step = 0
63 options.l = None 66 options.last_push = None
64 options.f = True 67 options.last_bleeding_edge = None
65 options.m = False 68 options.force = True
66 options.r = reviewer 69 options.manual = False
67 options.c = chrome_path 70 options.chromium = chrome_path
71 options.reviewer = reviewer
72 options.author = author
68 return PushToTrunkOptions(options) 73 return PushToTrunkOptions(options)
69 74
70 def __init__(self, options): 75 def __init__(self, options):
71 super(PushToTrunkOptions, self).__init__(options, options.m) 76 super(PushToTrunkOptions, self).__init__(options, options.manual)
72 self.requires_editor = not options.f 77 self.requires_editor = not options.force
73 self.wait_for_lgtm = not options.f 78 self.wait_for_lgtm = not options.force
74 self.tbr_commit = not options.m 79 self.tbr_commit = not options.manual
75 self.l = options.l 80 self.last_push = options.last_push
76 self.r = options.r 81 self.reviewer = options.reviewer
77 self.c = options.c 82 self.chromium = options.chromium
83 self.last_bleeding_edge = getattr(options, 'last_bleeding_edge', None)
84 self.author = getattr(options, 'author', None)
85
78 86
79 class Preparation(Step): 87 class Preparation(Step):
80 MESSAGE = "Preparation." 88 MESSAGE = "Preparation."
81 89
82 def RunStep(self): 90 def RunStep(self):
83 self.InitialEnvironmentChecks() 91 self.InitialEnvironmentChecks()
84 self.CommonPrepare() 92 self.CommonPrepare()
85 self.PrepareBranch() 93 self.PrepareBranch()
86 self.DeleteBranch(self.Config(TRUNKBRANCH)) 94 self.DeleteBranch(self.Config(TRUNKBRANCH))
87 95
88 96
89 class FreshBranch(Step): 97 class FreshBranch(Step):
90 MESSAGE = "Create a fresh branch." 98 MESSAGE = "Create a fresh branch."
91 99
92 def RunStep(self): 100 def RunStep(self):
93 args = "checkout -b %s svn/bleeding_edge" % self.Config(BRANCHNAME) 101 self.GitCreateBranch(self.Config(BRANCHNAME), "svn/bleeding_edge")
94 if self.Git(args) is None:
95 self.Die("Creating branch %s failed." % self.Config(BRANCHNAME))
96 102
97 103
98 class DetectLastPush(Step): 104 class DetectLastPush(Step):
99 MESSAGE = "Detect commit ID of last push to trunk." 105 MESSAGE = "Detect commit ID of last push to trunk."
100 106
101 def RunStep(self): 107 def RunStep(self):
102 last_push = (self._options.l or 108 last_push = self._options.last_push or self.FindLastTrunkPush()
103 self.Git("log -1 --format=%H ChangeLog").strip())
104 while True: 109 while True:
105 # Print assumed commit, circumventing git's pager. 110 # Print assumed commit, circumventing git's pager.
106 print self.Git("log -1 %s" % last_push) 111 print self.GitLog(n=1, git_hash=last_push)
107 if self.Confirm("Is the commit printed above the last push to trunk?"): 112 if self.Confirm("Is the commit printed above the last push to trunk?"):
108 break 113 break
109 args = "log -1 --format=%H %s^ ChangeLog" % last_push 114 last_push = self.FindLastTrunkPush(parent_hash=last_push)
110 last_push = self.Git(args).strip() 115
111 self.Persist("last_push", last_push) 116 if self._options.last_bleeding_edge:
112 self._state["last_push"] = last_push 117 # Read the bleeding edge revision of the last push from a command-line
118 # option.
119 last_push_bleeding_edge = self._options.last_bleeding_edge
120 else:
121 # Retrieve the bleeding edge revision of the last push from the text in
122 # the push commit message.
123 last_push_title = self.GitLog(n=1, format="%s", git_hash=last_push)
124 last_push_be_svn = PUSH_MESSAGE_RE.match(last_push_title).group(1)
125 if not last_push_be_svn:
126 self.Die("Could not retrieve bleeding edge revision for trunk push %s"
127 % last_push)
128 last_push_bleeding_edge = self.GitSVNFindGitHash(last_push_be_svn)
129 if not last_push_bleeding_edge:
130 self.Die("Could not retrieve bleeding edge git hash for trunk push %s"
131 % last_push)
132
133 # TODO(machenbach): last_push_trunk points to the svn revision on trunk.
134 # It is not used yet but we'll need it for retrieving the current version.
135 self["last_push_trunk"] = last_push
136 # TODO(machenbach): This currently points to the prepare push revision that
137 # will be deprecated soon. After the deprecation it will point to the last
138 # bleeding_edge revision that went into the last push.
139 self["last_push_bleeding_edge"] = last_push_bleeding_edge
113 140
114 141
115 class PrepareChangeLog(Step): 142 class PrepareChangeLog(Step):
116 MESSAGE = "Prepare raw ChangeLog entry." 143 MESSAGE = "Prepare raw ChangeLog entry."
117 144
118 def Reload(self, body): 145 def Reload(self, body):
119 """Attempts to reload the commit message from rietveld in order to allow 146 """Attempts to reload the commit message from rietveld in order to allow
120 late changes to the LOG flag. Note: This is brittle to future changes of 147 late changes to the LOG flag. Note: This is brittle to future changes of
121 the web page name or structure. 148 the web page name or structure.
122 """ 149 """
123 match = re.search(r"^Review URL: https://codereview\.chromium\.org/(\d+)$", 150 match = re.search(r"^Review URL: https://codereview\.chromium\.org/(\d+)$",
124 body, flags=re.M) 151 body, flags=re.M)
125 if match: 152 if match:
126 cl_url = "https://codereview.chromium.org/%s/description" % match.group(1) 153 cl_url = ("https://codereview.chromium.org/%s/description"
154 % match.group(1))
127 try: 155 try:
128 # Fetch from Rietveld but only retry once with one second delay since 156 # Fetch from Rietveld but only retry once with one second delay since
129 # there might be many revisions. 157 # there might be many revisions.
130 body = self.ReadURL(cl_url, wait_plan=[1]) 158 body = self.ReadURL(cl_url, wait_plan=[1])
131 except urllib2.URLError: 159 except urllib2.URLError:
132 pass 160 pass
133 return body 161 return body
134 162
135 def RunStep(self): 163 def RunStep(self):
136 self.RestoreIfUnset("last_push")
137
138 # These version numbers are used again later for the trunk commit. 164 # These version numbers are used again later for the trunk commit.
139 self.ReadAndPersistVersion() 165 self.ReadAndPersistVersion()
140 166 self["date"] = self.GetDate()
141 date = self.GetDate() 167 self["version"] = "%s.%s.%s" % (self["major"],
142 self.Persist("date", date) 168 self["minor"],
143 output = "%s: Version %s.%s.%s\n\n" % (date, 169 self["build"])
144 self._state["major"], 170 output = "%s: Version %s\n\n" % (self["date"],
145 self._state["minor"], 171 self["version"])
146 self._state["build"])
147 TextToFile(output, self.Config(CHANGELOG_ENTRY_FILE)) 172 TextToFile(output, self.Config(CHANGELOG_ENTRY_FILE))
148 173 commits = self.GitLog(format="%H",
149 args = "log %s..HEAD --format=%%H" % self._state["last_push"] 174 git_hash="%s..HEAD" % self["last_push_bleeding_edge"])
150 commits = self.Git(args).strip()
151 175
152 # Cache raw commit messages. 176 # Cache raw commit messages.
153 commit_messages = [ 177 commit_messages = [
154 [ 178 [
155 self.Git("log -1 %s --format=\"%%s\"" % commit), 179 self.GitLog(n=1, format="%s", git_hash=commit),
156 self.Reload(self.Git("log -1 %s --format=\"%%B\"" % commit)), 180 self.Reload(self.GitLog(n=1, format="%B", git_hash=commit)),
157 self.Git("log -1 %s --format=\"%%an\"" % commit), 181 self.GitLog(n=1, format="%an", git_hash=commit),
158 ] for commit in commits.splitlines() 182 ] for commit in commits.splitlines()
159 ] 183 ]
160 184
161 # Auto-format commit messages. 185 # Auto-format commit messages.
162 body = MakeChangeLogBody(commit_messages, auto_format=True) 186 body = MakeChangeLogBody(commit_messages, auto_format=True)
163 AppendToFile(body, self.Config(CHANGELOG_ENTRY_FILE)) 187 AppendToFile(body, self.Config(CHANGELOG_ENTRY_FILE))
164 188
165 msg = (" Performance and stability improvements on all platforms." 189 msg = (" Performance and stability improvements on all platforms."
166 "\n#\n# The change log above is auto-generated. Please review if " 190 "\n#\n# The change log above is auto-generated. Please review if "
167 "all relevant\n# commit messages from the list below are included." 191 "all relevant\n# commit messages from the list below are included."
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
200 224
201 AppendToFile(FileToText(self.Config(CHANGELOG_FILE)), new_changelog) 225 AppendToFile(FileToText(self.Config(CHANGELOG_FILE)), new_changelog)
202 TextToFile(FileToText(new_changelog), self.Config(CHANGELOG_FILE)) 226 TextToFile(FileToText(new_changelog), self.Config(CHANGELOG_FILE))
203 os.remove(new_changelog) 227 os.remove(new_changelog)
204 228
205 229
206 class IncrementVersion(Step): 230 class IncrementVersion(Step):
207 MESSAGE = "Increment version number." 231 MESSAGE = "Increment version number."
208 232
209 def RunStep(self): 233 def RunStep(self):
210 self.RestoreIfUnset("build") 234 new_build = str(int(self["build"]) + 1)
211 new_build = str(int(self._state["build"]) + 1)
212 235
213 if self.Confirm(("Automatically increment BUILD_NUMBER? (Saying 'n' will " 236 if self.Confirm(("Automatically increment BUILD_NUMBER? (Saying 'n' will "
214 "fire up your EDITOR on %s so you can make arbitrary " 237 "fire up your EDITOR on %s so you can make arbitrary "
215 "changes. When you're done, save the file and exit your " 238 "changes. When you're done, save the file and exit your "
216 "EDITOR.)" % self.Config(VERSION_FILE))): 239 "EDITOR.)" % self.Config(VERSION_FILE))):
217 text = FileToText(self.Config(VERSION_FILE)) 240 text = FileToText(self.Config(VERSION_FILE))
218 text = MSub(r"(?<=#define BUILD_NUMBER)(?P<space>\s+)\d*$", 241 text = MSub(r"(?<=#define BUILD_NUMBER)(?P<space>\s+)\d*$",
219 r"\g<space>%s" % new_build, 242 r"\g<space>%s" % new_build,
220 text) 243 text)
221 TextToFile(text, self.Config(VERSION_FILE)) 244 TextToFile(text, self.Config(VERSION_FILE))
222 else: 245 else:
223 self.Editor(self.Config(VERSION_FILE)) 246 self.Editor(self.Config(VERSION_FILE))
224 247
225 self.ReadAndPersistVersion("new_") 248 self.ReadAndPersistVersion("new_")
226 249
227 250
228 class CommitLocal(Step): 251 class CommitLocal(Step):
229 MESSAGE = "Commit to local branch." 252 MESSAGE = "Commit to local branch."
230 253
231 def RunStep(self): 254 def RunStep(self):
232 self.RestoreVersionIfUnset("new_") 255 self["prep_commit_msg"] = ("Prepare push to trunk. "
233 prep_commit_msg = ("Prepare push to trunk. " 256 "Now working on version %s.%s.%s." % (self["new_major"],
234 "Now working on version %s.%s.%s." % (self._state["new_major"], 257 self["new_minor"],
235 self._state["new_minor"], 258 self["new_build"]))
236 self._state["new_build"]))
237 self.Persist("prep_commit_msg", prep_commit_msg)
238 259
239 # Include optional TBR only in the git command. The persisted commit 260 # Include optional TBR only in the git command. The persisted commit
240 # message is used for finding the commit again later. 261 # message is used for finding the commit again later.
241 review = "\n\nTBR=%s" % self._options.r if self._options.tbr_commit else "" 262 if self._options.tbr_commit:
242 if self.Git("commit -a -m \"%s%s\"" % (prep_commit_msg, review)) is None: 263 message = "%s\n\nTBR=%s" % (self["prep_commit_msg"],
243 self.Die("'git commit -a' failed.") 264 self._options.reviewer)
265 else:
266 message = "%s" % self["prep_commit_msg"]
267 self.GitCommit(message)
244 268
245 269
246 class CommitRepository(Step): 270 class CommitRepository(Step):
247 MESSAGE = "Commit to the repository." 271 MESSAGE = "Commit to the repository."
248 272
249 def RunStep(self): 273 def RunStep(self):
250 self.WaitForLGTM() 274 self.WaitForLGTM()
251 # Re-read the ChangeLog entry (to pick up possible changes). 275 # Re-read the ChangeLog entry (to pick up possible changes).
252 # FIXME(machenbach): This was hanging once with a broken pipe. 276 # FIXME(machenbach): This was hanging once with a broken pipe.
253 TextToFile(GetLastChangeLogEntries(self.Config(CHANGELOG_FILE)), 277 TextToFile(GetLastChangeLogEntries(self.Config(CHANGELOG_FILE)),
254 self.Config(CHANGELOG_ENTRY_FILE)) 278 self.Config(CHANGELOG_ENTRY_FILE))
255 279
256 if self.Git("cl presubmit", "PRESUBMIT_TREE_CHECK=\"skip\"") is None: 280 self.GitPresubmit()
257 self.Die("'git cl presubmit' failed, please try again.") 281 self.GitDCommit()
258
259 if self.Git("cl dcommit -f --bypass-hooks") is None:
260 self.Die("'git cl dcommit' failed, please try again.")
261 282
262 283
263 class StragglerCommits(Step): 284 class StragglerCommits(Step):
264 MESSAGE = ("Fetch straggler commits that sneaked in since this script was " 285 MESSAGE = ("Fetch straggler commits that sneaked in since this script was "
265 "started.") 286 "started.")
266 287
267 def RunStep(self): 288 def RunStep(self):
268 if self.Git("svn fetch") is None: 289 self.GitSVNFetch()
269 self.Die("'git svn fetch' failed.") 290 self.GitCheckout("svn/bleeding_edge")
270 self.Git("checkout svn/bleeding_edge") 291 self["prepare_commit_hash"] = self.GitLog(n=1, format="%H",
271 self.RestoreIfUnset("prep_commit_msg") 292 grep=self["prep_commit_msg"])
272 args = "log -1 --format=%%H --grep=\"%s\"" % self._state["prep_commit_msg"]
273 prepare_commit_hash = self.Git(args).strip()
274 self.Persist("prepare_commit_hash", prepare_commit_hash)
275 293
276 294
277 class SquashCommits(Step): 295 class SquashCommits(Step):
278 MESSAGE = "Squash commits into one." 296 MESSAGE = "Squash commits into one."
279 297
280 def RunStep(self): 298 def RunStep(self):
281 # Instead of relying on "git rebase -i", we'll just create a diff, because 299 # Instead of relying on "git rebase -i", we'll just create a diff, because
282 # that's easier to automate. 300 # that's easier to automate.
283 self.RestoreIfUnset("prepare_commit_hash") 301 TextToFile(self.GitDiff("svn/trunk", self["prepare_commit_hash"]),
284 args = "diff svn/trunk %s" % self._state["prepare_commit_hash"] 302 self.Config(PATCH_FILE))
285 TextToFile(self.Git(args), self.Config(PATCH_FILE))
286 303
287 # Convert the ChangeLog entry to commit message format. 304 # Convert the ChangeLog entry to commit message format.
288 self.RestoreIfUnset("date")
289 text = FileToText(self.Config(CHANGELOG_ENTRY_FILE)) 305 text = FileToText(self.Config(CHANGELOG_ENTRY_FILE))
290 306
291 # Remove date and trailing white space. 307 # Remove date and trailing white space.
292 text = re.sub(r"^%s: " % self._state["date"], "", text.rstrip()) 308 text = re.sub(r"^%s: " % self["date"], "", text.rstrip())
309
310 # Retrieve svn revision for showing the used bleeding edge revision in the
311 # commit message.
312 self["svn_revision"] = self.GitSVNFindSVNRev(self["prepare_commit_hash"])
313 suffix = PUSH_MESSAGE_SUFFIX % int(self["svn_revision"])
314 text = MSub(r"^(Version \d+\.\d+\.\d+)$", "\\1%s" % suffix, text)
293 315
294 # Remove indentation and merge paragraphs into single long lines, keeping 316 # Remove indentation and merge paragraphs into single long lines, keeping
295 # empty lines between them. 317 # empty lines between them.
296 def SplitMapJoin(split_text, fun, join_text): 318 def SplitMapJoin(split_text, fun, join_text):
297 return lambda text: join_text.join(map(fun, text.split(split_text))) 319 return lambda text: join_text.join(map(fun, text.split(split_text)))
298 strip = lambda line: line.strip() 320 strip = lambda line: line.strip()
299 text = SplitMapJoin("\n\n", SplitMapJoin("\n", strip, " "), "\n\n")(text) 321 text = SplitMapJoin("\n\n", SplitMapJoin("\n", strip, " "), "\n\n")(text)
300 322
301 if not text: 323 if not text:
302 self.Die("Commit message editing failed.") 324 self.Die("Commit message editing failed.")
303 TextToFile(text, self.Config(COMMITMSG_FILE)) 325 TextToFile(text, self.Config(COMMITMSG_FILE))
304 os.remove(self.Config(CHANGELOG_ENTRY_FILE)) 326 os.remove(self.Config(CHANGELOG_ENTRY_FILE))
305 327
306 328
307 class NewBranch(Step): 329 class NewBranch(Step):
308 MESSAGE = "Create a new branch from trunk." 330 MESSAGE = "Create a new branch from trunk."
309 331
310 def RunStep(self): 332 def RunStep(self):
311 if self.Git("checkout -b %s svn/trunk" % self.Config(TRUNKBRANCH)) is None: 333 self.GitCreateBranch(self.Config(TRUNKBRANCH), "svn/trunk")
312 self.Die("Checking out a new branch '%s' failed." %
313 self.Config(TRUNKBRANCH))
314 334
315 335
316 class ApplyChanges(Step): 336 class ApplyChanges(Step):
317 MESSAGE = "Apply squashed changes." 337 MESSAGE = "Apply squashed changes."
318 338
319 def RunStep(self): 339 def RunStep(self):
320 self.ApplyPatch(self.Config(PATCH_FILE)) 340 self.ApplyPatch(self.Config(PATCH_FILE))
321 Command("rm", "-f %s*" % self.Config(PATCH_FILE)) 341 Command("rm", "-f %s*" % self.Config(PATCH_FILE))
322 342
323 343
324 class SetVersion(Step): 344 class SetVersion(Step):
325 MESSAGE = "Set correct version for trunk." 345 MESSAGE = "Set correct version for trunk."
326 346
327 def RunStep(self): 347 def RunStep(self):
328 self.RestoreVersionIfUnset()
329 output = "" 348 output = ""
330 for line in FileToText(self.Config(VERSION_FILE)).splitlines(): 349 for line in FileToText(self.Config(VERSION_FILE)).splitlines():
331 if line.startswith("#define MAJOR_VERSION"): 350 if line.startswith("#define MAJOR_VERSION"):
332 line = re.sub("\d+$", self._state["major"], line) 351 line = re.sub("\d+$", self["major"], line)
333 elif line.startswith("#define MINOR_VERSION"): 352 elif line.startswith("#define MINOR_VERSION"):
334 line = re.sub("\d+$", self._state["minor"], line) 353 line = re.sub("\d+$", self["minor"], line)
335 elif line.startswith("#define BUILD_NUMBER"): 354 elif line.startswith("#define BUILD_NUMBER"):
336 line = re.sub("\d+$", self._state["build"], line) 355 line = re.sub("\d+$", self["build"], line)
337 elif line.startswith("#define PATCH_LEVEL"): 356 elif line.startswith("#define PATCH_LEVEL"):
338 line = re.sub("\d+$", "0", line) 357 line = re.sub("\d+$", "0", line)
339 elif line.startswith("#define IS_CANDIDATE_VERSION"): 358 elif line.startswith("#define IS_CANDIDATE_VERSION"):
340 line = re.sub("\d+$", "0", line) 359 line = re.sub("\d+$", "0", line)
341 output += "%s\n" % line 360 output += "%s\n" % line
342 TextToFile(output, self.Config(VERSION_FILE)) 361 TextToFile(output, self.Config(VERSION_FILE))
343 362
344 363
345 class CommitTrunk(Step): 364 class CommitTrunk(Step):
346 MESSAGE = "Commit to local trunk branch." 365 MESSAGE = "Commit to local trunk branch."
347 366
348 def RunStep(self): 367 def RunStep(self):
349 self.Git("add \"%s\"" % self.Config(VERSION_FILE)) 368 self.GitAdd(self.Config(VERSION_FILE))
350 if self.Git("commit -F \"%s\"" % self.Config(COMMITMSG_FILE)) is None: 369 self.GitCommit(file_name = self.Config(COMMITMSG_FILE))
351 self.Die("'git commit' failed.")
352 Command("rm", "-f %s*" % self.Config(COMMITMSG_FILE)) 370 Command("rm", "-f %s*" % self.Config(COMMITMSG_FILE))
353 371
354 372
355 class SanityCheck(Step): 373 class SanityCheck(Step):
356 MESSAGE = "Sanity check." 374 MESSAGE = "Sanity check."
357 375
358 def RunStep(self): 376 def RunStep(self):
359 if not self.Confirm("Please check if your local checkout is sane: Inspect " 377 if not self.Confirm("Please check if your local checkout is sane: Inspect "
360 "%s, compile, run tests. Do you want to commit this new trunk " 378 "%s, compile, run tests. Do you want to commit this new trunk "
361 "revision to the repository?" % self.Config(VERSION_FILE)): 379 "revision to the repository?" % self.Config(VERSION_FILE)):
362 self.Die("Execution canceled.") 380 self.Die("Execution canceled.")
363 381
364 382
365 class CommitSVN(Step): 383 class CommitSVN(Step):
366 MESSAGE = "Commit to SVN." 384 MESSAGE = "Commit to SVN."
367 385
368 def RunStep(self): 386 def RunStep(self):
369 result = self.Git("svn dcommit 2>&1") 387 result = self.GitSVNDCommit()
370 if not result: 388 if not result:
371 self.Die("'git svn dcommit' failed.") 389 self.Die("'git svn dcommit' failed.")
372 result = filter(lambda x: re.search(r"^Committed r[0-9]+", x), 390 result = filter(lambda x: re.search(r"^Committed r[0-9]+", x),
373 result.splitlines()) 391 result.splitlines())
374 if len(result) > 0: 392 if len(result) > 0:
375 trunk_revision = re.sub(r"^Committed r([0-9]+)", r"\1", result[0]) 393 self["trunk_revision"] = re.sub(r"^Committed r([0-9]+)", r"\1",result[0])
376 394
377 # Sometimes grepping for the revision fails. No idea why. If you figure 395 # Sometimes grepping for the revision fails. No idea why. If you figure
378 # out why it is flaky, please do fix it properly. 396 # out why it is flaky, please do fix it properly.
379 if not trunk_revision: 397 if not self["trunk_revision"]:
380 print("Sorry, grepping for the SVN revision failed. Please look for it " 398 print("Sorry, grepping for the SVN revision failed. Please look for it "
381 "in the last command's output above and provide it manually (just " 399 "in the last command's output above and provide it manually (just "
382 "the number, without the leading \"r\").") 400 "the number, without the leading \"r\").")
383 self.DieNoManualMode("Can't prompt in forced mode.") 401 self.DieNoManualMode("Can't prompt in forced mode.")
384 while not trunk_revision: 402 while not self["trunk_revision"]:
385 print "> ", 403 print "> ",
386 trunk_revision = self.ReadLine() 404 self["trunk_revision"] = self.ReadLine()
387 self.Persist("trunk_revision", trunk_revision)
388 405
389 406
390 class TagRevision(Step): 407 class TagRevision(Step):
391 MESSAGE = "Tag the new revision." 408 MESSAGE = "Tag the new revision."
392 409
393 def RunStep(self): 410 def RunStep(self):
394 self.RestoreVersionIfUnset() 411 self.GitSVNTag(self["version"])
395 ver = "%s.%s.%s" % (self._state["major"],
396 self._state["minor"],
397 self._state["build"])
398 if self.Git("svn tag %s -m \"Tagging version %s\"" % (ver, ver)) is None:
399 self.Die("'git svn tag' failed.")
400 412
401 413
402 class CheckChromium(Step): 414 class CheckChromium(Step):
403 MESSAGE = "Ask for chromium checkout." 415 MESSAGE = "Ask for chromium checkout."
404 416
405 def Run(self): 417 def Run(self):
406 chrome_path = self._options.c 418 self["chrome_path"] = self._options.chromium
407 if not chrome_path: 419 if not self["chrome_path"]:
408 self.DieNoManualMode("Please specify the path to a Chromium checkout in " 420 self.DieNoManualMode("Please specify the path to a Chromium checkout in "
409 "forced mode.") 421 "forced mode.")
410 print ("Do you have a \"NewGit\" Chromium checkout and want " 422 print ("Do you have a \"NewGit\" Chromium checkout and want "
411 "this script to automate creation of the roll CL? If yes, enter the " 423 "this script to automate creation of the roll CL? If yes, enter the "
412 "path to (and including) the \"src\" directory here, otherwise just " 424 "path to (and including) the \"src\" directory here, otherwise just "
413 "press <Return>: "), 425 "press <Return>: "),
414 chrome_path = self.ReadLine() 426 self["chrome_path"] = self.ReadLine()
415 self.Persist("chrome_path", chrome_path)
416 427
417 428
418 class SwitchChromium(Step): 429 class SwitchChromium(Step):
419 MESSAGE = "Switch to Chromium checkout." 430 MESSAGE = "Switch to Chromium checkout."
420 REQUIRES = "chrome_path" 431 REQUIRES = "chrome_path"
421 432
422 def RunStep(self): 433 def RunStep(self):
423 v8_path = os.getcwd() 434 self["v8_path"] = os.getcwd()
424 self.Persist("v8_path", v8_path) 435 os.chdir(self["chrome_path"])
425 os.chdir(self._state["chrome_path"])
426 self.InitialEnvironmentChecks() 436 self.InitialEnvironmentChecks()
427 # Check for a clean workdir. 437 # Check for a clean workdir.
428 if self.Git("status -s -uno").strip() != "": 438 if not self.GitIsWorkdirClean():
429 self.Die("Workspace is not clean. Please commit or undo your changes.") 439 self.Die("Workspace is not clean. Please commit or undo your changes.")
430 # Assert that the DEPS file is there. 440 # Assert that the DEPS file is there.
431 if not os.path.exists(self.Config(DEPS_FILE)): 441 if not os.path.exists(self.Config(DEPS_FILE)):
432 self.Die("DEPS file not present.") 442 self.Die("DEPS file not present.")
433 443
434 444
435 class UpdateChromiumCheckout(Step): 445 class UpdateChromiumCheckout(Step):
436 MESSAGE = "Update the checkout and create a new branch." 446 MESSAGE = "Update the checkout and create a new branch."
437 REQUIRES = "chrome_path" 447 REQUIRES = "chrome_path"
438 448
439 def RunStep(self): 449 def RunStep(self):
440 os.chdir(self._state["chrome_path"]) 450 os.chdir(self["chrome_path"])
441 if self.Git("checkout master") is None: 451 self.GitCheckout("master")
442 self.Die("'git checkout master' failed.") 452 self.GitPull()
443 if self.Git("pull") is None: 453 self.GitCreateBranch("v8-roll-%s" % self["trunk_revision"])
444 self.Die("'git pull' failed, please try again.")
445
446 self.RestoreIfUnset("trunk_revision")
447 args = "checkout -b v8-roll-%s" % self._state["trunk_revision"]
448 if self.Git(args) is None:
449 self.Die("Failed to checkout a new branch.")
450 454
451 455
452 class UploadCL(Step): 456 class UploadCL(Step):
453 MESSAGE = "Create and upload CL." 457 MESSAGE = "Create and upload CL."
454 REQUIRES = "chrome_path" 458 REQUIRES = "chrome_path"
455 459
456 def RunStep(self): 460 def RunStep(self):
457 os.chdir(self._state["chrome_path"]) 461 os.chdir(self["chrome_path"])
458 462
459 # Patch DEPS file. 463 # Patch DEPS file.
460 self.RestoreIfUnset("trunk_revision")
461 deps = FileToText(self.Config(DEPS_FILE)) 464 deps = FileToText(self.Config(DEPS_FILE))
462 deps = re.sub("(?<=\"v8_revision\": \")([0-9]+)(?=\")", 465 deps = re.sub("(?<=\"v8_revision\": \")([0-9]+)(?=\")",
463 self._state["trunk_revision"], 466 self["trunk_revision"],
464 deps) 467 deps)
465 TextToFile(deps, self.Config(DEPS_FILE)) 468 TextToFile(deps, self.Config(DEPS_FILE))
466 469
467 self.RestoreVersionIfUnset() 470 if self._options.reviewer:
468 ver = "%s.%s.%s" % (self._state["major"], 471 print "Using account %s for review." % self._options.reviewer
469 self._state["minor"], 472 rev = self._options.reviewer
470 self._state["build"])
471 if self._options.r:
472 print "Using account %s for review." % self._options.r
473 rev = self._options.r
474 else: 473 else:
475 print "Please enter the email address of a reviewer for the roll CL: ", 474 print "Please enter the email address of a reviewer for the roll CL: ",
476 self.DieNoManualMode("A reviewer must be specified in forced mode.") 475 self.DieNoManualMode("A reviewer must be specified in forced mode.")
477 rev = self.ReadLine() 476 rev = self.ReadLine()
478 args = "commit -am \"Update V8 to version %s.\n\nTBR=%s\"" % (ver, rev) 477 suffix = PUSH_MESSAGE_SUFFIX % int(self["svn_revision"])
479 if self.Git(args) is None: 478 self.GitCommit("Update V8 to version %s%s.\n\nTBR=%s"
480 self.Die("'git commit' failed.") 479 % (self["version"], suffix, rev))
481 force_flag = " -f" if self._options.force_upload else "" 480 self.GitUpload(author=self._options.author,
482 if self.Git("cl upload --send-mail%s" % force_flag, pipe=False) is None: 481 force=self._options.force_upload)
483 self.Die("'git cl upload' failed, please try again.")
484 print "CL uploaded." 482 print "CL uploaded."
485 483
486 484
487 class SwitchV8(Step): 485 class SwitchV8(Step):
488 MESSAGE = "Returning to V8 checkout." 486 MESSAGE = "Returning to V8 checkout."
489 REQUIRES = "chrome_path" 487 REQUIRES = "chrome_path"
490 488
491 def RunStep(self): 489 def RunStep(self):
492 self.RestoreIfUnset("v8_path") 490 os.chdir(self["v8_path"])
493 os.chdir(self._state["v8_path"])
494 491
495 492
496 class CleanUp(Step): 493 class CleanUp(Step):
497 MESSAGE = "Done!" 494 MESSAGE = "Done!"
498 495
499 def RunStep(self): 496 def RunStep(self):
500 self.RestoreVersionIfUnset() 497 if self["chrome_path"]:
501 ver = "%s.%s.%s" % (self._state["major"],
502 self._state["minor"],
503 self._state["build"])
504 self.RestoreIfUnset("trunk_revision")
505 self.RestoreIfUnset("chrome_path")
506
507 if self._state["chrome_path"]:
508 print("Congratulations, you have successfully created the trunk " 498 print("Congratulations, you have successfully created the trunk "
509 "revision %s and rolled it into Chromium. Please don't forget to " 499 "revision %s and rolled it into Chromium. Please don't forget to "
510 "update the v8rel spreadsheet:" % ver) 500 "update the v8rel spreadsheet:" % self["version"])
511 else: 501 else:
512 print("Congratulations, you have successfully created the trunk " 502 print("Congratulations, you have successfully created the trunk "
513 "revision %s. Please don't forget to roll this new version into " 503 "revision %s. Please don't forget to roll this new version into "
514 "Chromium, and to update the v8rel spreadsheet:" % ver) 504 "Chromium, and to update the v8rel spreadsheet:"
515 print "%s\ttrunk\t%s" % (ver, self._state["trunk_revision"]) 505 % self["version"])
506 print "%s\ttrunk\t%s" % (self["version"],
507 self["trunk_revision"])
516 508
517 self.CommonCleanup() 509 self.CommonCleanup()
518 if self.Config(TRUNKBRANCH) != self._state["current_branch"]: 510 if self.Config(TRUNKBRANCH) != self["current_branch"]:
519 self.Git("branch -D %s" % self.Config(TRUNKBRANCH)) 511 self.GitDeleteBranch(self.Config(TRUNKBRANCH))
520 512
521 513
522 def RunPushToTrunk(config, 514 def RunPushToTrunk(config,
523 options, 515 options,
524 side_effect_handler=DEFAULT_SIDE_EFFECT_HANDLER): 516 side_effect_handler=DEFAULT_SIDE_EFFECT_HANDLER):
525 step_classes = [ 517 step_classes = [
526 Preparation, 518 Preparation,
527 FreshBranch, 519 FreshBranch,
528 DetectLastPush, 520 DetectLastPush,
529 PrepareChangeLog, 521 PrepareChangeLog,
(...skipping 16 matching lines...) Expand all
546 UpdateChromiumCheckout, 538 UpdateChromiumCheckout,
547 UploadCL, 539 UploadCL,
548 SwitchV8, 540 SwitchV8,
549 CleanUp, 541 CleanUp,
550 ] 542 ]
551 543
552 RunScript(step_classes, config, options, side_effect_handler) 544 RunScript(step_classes, config, options, side_effect_handler)
553 545
554 546
555 def BuildOptions(): 547 def BuildOptions():
556 result = optparse.OptionParser() 548 parser = argparse.ArgumentParser()
557 result.add_option("-c", "--chromium", dest="c", 549 group = parser.add_mutually_exclusive_group()
558 help=("Specify the path to your Chromium src/ " 550 group.add_argument("-f", "--force",
559 "directory to automate the V8 roll.")) 551 help="Don't prompt the user.",
560 result.add_option("-f", "--force", dest="f", 552 default=False, action="store_true")
561 help="Don't prompt the user.", 553 group.add_argument("-m", "--manual",
562 default=False, action="store_true") 554 help="Prompt the user at every important step.",
563 result.add_option("-l", "--last-push", dest="l", 555 default=False, action="store_true")
564 help=("Manually specify the git commit ID " 556 parser.add_argument("-a", "--author",
565 "of the last push to trunk.")) 557 help="The author email used for rietveld.")
566 result.add_option("-m", "--manual", dest="m", 558 parser.add_argument("-b", "--last-bleeding-edge",
567 help="Prompt the user at every important step.", 559 help=("The git commit ID of the last bleeding edge "
568 default=False, action="store_true") 560 "revision that was pushed to trunk. This is used "
569 result.add_option("-r", "--reviewer", dest="r", 561 "for the auto-generated ChangeLog entry."))
570 help=("Specify the account name to be used for reviews.")) 562 parser.add_argument("-c", "--chromium",
571 result.add_option("-s", "--step", dest="s", 563 help=("The path to your Chromium src/ directory to "
572 help="Specify the step where to start work. Default: 0.", 564 "automate the V8 roll."))
573 default=0, type="int") 565 parser.add_argument("-l", "--last-push",
574 return result 566 help="The git commit ID of the last push to trunk.")
567 parser.add_argument("-r", "--reviewer",
568 help="The account name to be used for reviews.")
569 parser.add_argument("-s", "--step",
570 help="The step where to start work. Default: 0.",
571 default=0, type=int)
572 return parser
575 573
576 574
577 def ProcessOptions(options): 575 def ProcessOptions(options):
578 if options.s < 0: 576 if options.step < 0:
579 print "Bad step number %d" % options.s 577 print "Bad step number %d" % options.step
580 return False 578 return False
581 if not options.m and not options.r: 579 if not options.manual and not options.reviewer:
582 print "A reviewer (-r) is required in (semi-)automatic mode." 580 print "A reviewer (-r) is required in (semi-)automatic mode."
583 return False 581 return False
584 if options.f and options.m: 582 if not options.manual and not options.chromium:
585 print "Manual and forced mode cannot be combined." 583 print "A chromium checkout (-c) is required in (semi-)automatic mode."
586 return False 584 return False
587 if not options.m and not options.c: 585 if not options.manual and not options.author:
588 print "A chromium checkout (-c) is required in (semi-)automatic mode." 586 print "Specify your chromium.org email with -a in (semi-)automatic mode."
589 return False 587 return False
590 return True 588 return True
591 589
592 590
593 def Main(): 591 def Main():
594 parser = BuildOptions() 592 parser = BuildOptions()
595 (options, args) = parser.parse_args() 593 options = parser.parse_args()
596 if not ProcessOptions(options): 594 if not ProcessOptions(options):
597 parser.print_help() 595 parser.print_help()
598 return 1 596 return 1
599 RunPushToTrunk(CONFIG, PushToTrunkOptions(options)) 597 RunPushToTrunk(CONFIG, PushToTrunkOptions(options))
600 598
601 if __name__ == "__main__": 599 if __name__ == "__main__":
602 sys.exit(Main()) 600 sys.exit(Main())
OLDNEW
« no previous file with comments | « tools/push-to-trunk/merge_to_branch.py ('k') | tools/push-to-trunk/test_scripts.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698