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

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

Issue 1398033003: [Release] Update merge script to leverage auto-tag bot (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: Check for roll branches Created 4 years, 5 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/git_recipes.py ('k') | tools/release/roll_merge.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 2014 the V8 project authors. All rights reserved. 2 # Copyright 2014 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
(...skipping 29 matching lines...) Expand all
40 40
41 def RunStep(self): 41 def RunStep(self):
42 if os.path.exists(self.Config("ALREADY_MERGING_SENTINEL_FILE")): 42 if os.path.exists(self.Config("ALREADY_MERGING_SENTINEL_FILE")):
43 if self._options.force: 43 if self._options.force:
44 os.remove(self.Config("ALREADY_MERGING_SENTINEL_FILE")) 44 os.remove(self.Config("ALREADY_MERGING_SENTINEL_FILE"))
45 elif self._options.step == 0: # pragma: no cover 45 elif self._options.step == 0: # pragma: no cover
46 self.Die("A merge is already in progress") 46 self.Die("A merge is already in progress")
47 open(self.Config("ALREADY_MERGING_SENTINEL_FILE"), "a").close() 47 open(self.Config("ALREADY_MERGING_SENTINEL_FILE"), "a").close()
48 48
49 self.InitialEnvironmentChecks(self.default_cwd) 49 self.InitialEnvironmentChecks(self.default_cwd)
50 if self._options.branch: 50
51 self["merge_to_branch"] = self._options.branch 51 self["merge_to_branch"] = self._options.branch
52 else: # pragma: no cover
53 self.Die("Please specify a branch to merge to")
54 52
55 self.CommonPrepare() 53 self.CommonPrepare()
56 self.PrepareBranch() 54 self.PrepareBranch()
57 55
58 56
59 class CreateBranch(Step): 57 class CreateBranch(Step):
60 MESSAGE = "Create a fresh branch for the patch." 58 MESSAGE = "Create a fresh branch for the patch."
61 59
62 def RunStep(self): 60 def RunStep(self):
63 self.GitCreateBranch(self.Config("BRANCHNAME"), 61 self.GitCreateBranch(self.Config("BRANCHNAME"),
64 self.vc.RemoteBranch(self["merge_to_branch"])) 62 self.vc.RemoteBranch(self["merge_to_branch"]))
65 63
66 64
67 class SearchArchitecturePorts(Step): 65 class SearchArchitecturePorts(Step):
68 MESSAGE = "Search for corresponding architecture ports." 66 MESSAGE = "Search for corresponding architecture ports."
69 67
70 def RunStep(self): 68 def RunStep(self):
71 self["full_revision_list"] = list(OrderedDict.fromkeys( 69 self["full_revision_list"] = list(OrderedDict.fromkeys(
72 self._options.revisions)) 70 self._options.revisions))
73 port_revision_list = [] 71 port_revision_list = []
74 for revision in self["full_revision_list"]: 72 for revision in self["full_revision_list"]:
75 # Search for commits which matches the "Port XXX" pattern. 73 # Search for commits which matches the "Port XXX" pattern.
76 git_hashes = self.GitLog(reverse=True, format="%H", 74 git_hashes = self.GitLog(reverse=True, format="%H",
77 grep="Port %s" % revision, 75 grep="^[Pp]ort %s" % revision,
78 branch=self.vc.RemoteMasterBranch()) 76 branch=self.vc.RemoteMasterBranch())
79 for git_hash in git_hashes.splitlines(): 77 for git_hash in git_hashes.splitlines():
80 revision_title = self.GitLog(n=1, format="%s", git_hash=git_hash) 78 revision_title = self.GitLog(n=1, format="%s", git_hash=git_hash)
81 79
82 # Is this revision included in the original revision list? 80 # Is this revision included in the original revision list?
83 if git_hash in self["full_revision_list"]: 81 if git_hash in self["full_revision_list"]:
84 print("Found port of %s -> %s (already included): %s" 82 print("Found port of %s -> %s (already included): %s"
85 % (revision, git_hash, revision_title)) 83 % (revision, git_hash, revision_title))
86 else: 84 else:
87 print("Found port of %s -> %s: %s" 85 print("Found port of %s -> %s: %s"
88 % (revision, git_hash, revision_title)) 86 % (revision, git_hash, revision_title))
89 port_revision_list.append(git_hash) 87 port_revision_list.append(git_hash)
90 88
91 # Do we find any port? 89 # Do we find any port?
92 if len(port_revision_list) > 0: 90 if len(port_revision_list) > 0:
93 if self.Confirm("Automatically add corresponding ports (%s)?" 91 if self.Confirm("Automatically add corresponding ports (%s)?"
94 % ", ".join(port_revision_list)): 92 % ", ".join(port_revision_list)):
95 #: 'y': Add ports to revision list. 93 #: 'y': Add ports to revision list.
96 self["full_revision_list"].extend(port_revision_list) 94 self["full_revision_list"].extend(port_revision_list)
97 95
98 96
99 class CreateCommitMessage(Step): 97 class CreateCommitMessage(Step):
100 MESSAGE = "Create commit message." 98 MESSAGE = "Create commit message."
101 99
100 def _create_commit_description(self, commit_hash):
101 patch_merge_desc = self.GitLog(n=1, format="%s", git_hash=commit_hash)
Michael Achenbach 2016/07/21 13:56:37 Is this done twice? E.g. below?
Michael Hablich 2016/07/21 14:27:33 Done.
102 description = "Merged: " + patch_merge_desc + "\n"
103 description = description + "Revision: " + commit_hash + "\n\n"
Michael Achenbach 2016/07/21 13:56:37 nit: description += ... or to avoid multiple stri
Michael Hablich 2016/07/21 14:27:32 Done.
104 return description
105
102 def RunStep(self): 106 def RunStep(self):
103 107
104 # Stringify: ["abcde", "12345"] -> "abcde, 12345" 108 # Stringify: ["abcde", "12345"] -> "abcde, 12345"
105 self["revision_list"] = ", ".join(self["full_revision_list"]) 109 self["revision_list"] = ", ".join(self["full_revision_list"])
106 110
107 if not self["revision_list"]: # pragma: no cover 111 if not self["revision_list"]: # pragma: no cover
108 self.Die("Revision list is empty.") 112 self.Die("Revision list is empty.")
109 113
110 action_text = "Merged %s" 114 msg_pieces = []
111 115
112 # The commit message title is added below after the version is specified. 116 if len(self["full_revision_list"]) > 1:
113 msg_pieces = [ 117 self["commit_title"] = "Merged: Squashed multiple commits."
114 "\n".join(action_text % s for s in self["full_revision_list"]), 118 else:
115 ] 119 commit_hash = self["full_revision_list"][0]
116 msg_pieces.append("\n\n") 120 patch_merge_desc = self.GitLog(n=1, format="%s", git_hash=commit_hash)
121 title = "Merged: " + patch_merge_desc
Michael Achenbach 2016/07/21 13:56:37 nit: inline title or also patch_merge_desc
Michael Hablich 2016/07/21 14:27:32 Done.
122 self["commit_title"] = title
117 123
118 for commit_hash in self["full_revision_list"]: 124 for commit_hash in self["full_revision_list"]:
119 patch_merge_desc = self.GitLog(n=1, format="%s", git_hash=commit_hash) 125 msg_pieces.append(self._create_commit_description(commit_hash))
Michael Achenbach 2016/07/21 13:56:37 Don't we get a double title here?
Michael Hablich 2016/07/21 14:27:33 Done.
120 msg_pieces.append("%s\n\n" % patch_merge_desc)
121 126
122 bugs = [] 127 bugs = []
123 for commit_hash in self["full_revision_list"]: 128 for commit_hash in self["full_revision_list"]:
124 msg = self.GitLog(n=1, git_hash=commit_hash) 129 msg = self.GitLog(n=1, git_hash=commit_hash)
125 for bug in re.findall(r"^[ \t]*BUG[ \t]*=[ \t]*(.*?)[ \t]*$", msg, re.M): 130 for bug in re.findall(r"^[ \t]*BUG[ \t]*=[ \t]*(.*?)[ \t]*$", msg, re.M):
126 bugs.extend(s.strip() for s in bug.split(",")) 131 bugs.extend(s.strip() for s in bug.split(","))
127 bug_aggregate = ",".join(sorted(filter(lambda s: s and s != "none", bugs))) 132 bug_aggregate = ",".join(sorted(filter(lambda s: s and s != "none", bugs)))
128 if bug_aggregate: 133 if bug_aggregate:
129 msg_pieces.append("BUG=%s\nLOG=N\n" % bug_aggregate) 134 msg_pieces.append("BUG=%s\nLOG=N\n" % bug_aggregate)
130 135
131 self["new_commit_msg"] = "".join(msg_pieces) 136 self["new_commit_msg"] = "".join(msg_pieces)
132 137
133 138
134 class ApplyPatches(Step): 139 class ApplyPatches(Step):
135 MESSAGE = "Apply patches for selected revisions." 140 MESSAGE = "Apply patches for selected revisions."
136 141
137 def RunStep(self): 142 def RunStep(self):
138 for commit_hash in self["full_revision_list"]: 143 for commit_hash in self["full_revision_list"]:
139 print("Applying patch for %s to %s..." 144 print("Applying patch for %s to %s..."
140 % (commit_hash, self["merge_to_branch"])) 145 % (commit_hash, self["merge_to_branch"]))
141 patch = self.GitGetPatch(commit_hash) 146 patch = self.GitGetPatch(commit_hash)
142 TextToFile(patch, self.Config("TEMPORARY_PATCH_FILE")) 147 TextToFile(patch, self.Config("TEMPORARY_PATCH_FILE"))
143 self.ApplyPatch(self.Config("TEMPORARY_PATCH_FILE")) 148 self.ApplyPatch(self.Config("TEMPORARY_PATCH_FILE"))
144 if self._options.patch: 149 if self._options.patch:
145 self.ApplyPatch(self._options.patch) 150 self.ApplyPatch(self._options.patch)
146 151
147
148 class PrepareVersion(Step):
149 MESSAGE = "Prepare version file."
150
151 def RunStep(self):
152 # This is used to calculate the patch level increment.
153 self.ReadAndPersistVersion()
154
155
156 class IncrementVersion(Step):
157 MESSAGE = "Increment version number."
158
159 def RunStep(self):
160 new_patch = str(int(self["patch"]) + 1)
161 if self.Confirm("Automatically increment V8_PATCH_LEVEL? (Saying 'n' will "
162 "fire up your EDITOR on %s so you can make arbitrary "
163 "changes. When you're done, save the file and exit your "
164 "EDITOR.)" % VERSION_FILE):
165 text = FileToText(os.path.join(self.default_cwd, VERSION_FILE))
166 text = MSub(r"(?<=#define V8_PATCH_LEVEL)(?P<space>\s+)\d*$",
167 r"\g<space>%s" % new_patch,
168 text)
169 TextToFile(text, os.path.join(self.default_cwd, VERSION_FILE))
170 else:
171 self.Editor(os.path.join(self.default_cwd, VERSION_FILE))
172 self.ReadAndPersistVersion("new_")
173 self["version"] = "%s.%s.%s.%s" % (self["new_major"],
174 self["new_minor"],
175 self["new_build"],
176 self["new_patch"])
177
178
179 class CommitLocal(Step): 152 class CommitLocal(Step):
180 MESSAGE = "Commit to local branch." 153 MESSAGE = "Commit to local branch."
181 154
182 def RunStep(self): 155 def RunStep(self):
183 # Add a commit message title. 156 # Add a commit message title.
184 self["commit_title"] = "Version %s (cherry-pick)" % self["version"]
185 self["new_commit_msg"] = "%s\n\n%s" % (self["commit_title"], 157 self["new_commit_msg"] = "%s\n\n%s" % (self["commit_title"],
186 self["new_commit_msg"]) 158 self["new_commit_msg"])
187 TextToFile(self["new_commit_msg"], self.Config("COMMITMSG_FILE")) 159 TextToFile(self["new_commit_msg"], self.Config("COMMITMSG_FILE"))
188 self.GitCommit(file_name=self.Config("COMMITMSG_FILE")) 160 self.GitCommit(file_name=self.Config("COMMITMSG_FILE"))
189 161
162 class AddInformationalComment(Step):
163 MESSAGE = 'Show additional information.'
164
165 def RunStep(self):
166 message = ("Please note that you are using a new "
Michael Achenbach 2016/07/21 13:56:38 Suggestion for making this readable to lazy devs:
Michael Hablich 2016/07/21 14:27:33 Done.
167 "version of the script merge_to_branch.py. "
168 "This script will no longer automatically update include/v8-version.h "
169 "and create a tag. This is ok, the trustworthy autotag bot will "
170 "take care of that. The old script is still available in "
171 "tools/release/roll_merge.py.")
172
173 self.GitCLAddComment(message)
190 174
191 class CommitRepository(Step): 175 class CommitRepository(Step):
192 MESSAGE = "Commit to the repository." 176 MESSAGE = "Commit to the repository."
193 177
194 def RunStep(self): 178 def RunStep(self):
195 self.GitCheckout(self.Config("BRANCHNAME")) 179 self.GitCheckout(self.Config("BRANCHNAME"))
196 self.WaitForLGTM() 180 self.WaitForLGTM()
197 self.GitPresubmit() 181 self.GitPresubmit()
198 self.vc.CLLand() 182 self.vc.CLLand()
199 183
200
201 class TagRevision(Step):
202 MESSAGE = "Create the tag."
203
204 def RunStep(self):
205 print "Creating tag %s" % self["version"]
206 self.vc.Tag(self["version"],
207 self.vc.RemoteBranch(self["merge_to_branch"]),
208 self["commit_title"])
209
210
211 class CleanUp(Step): 184 class CleanUp(Step):
212 MESSAGE = "Cleanup." 185 MESSAGE = "Cleanup."
213 186
214 def RunStep(self): 187 def RunStep(self):
215 self.CommonCleanup() 188 self.CommonCleanup()
216 print "*** SUMMARY ***" 189 print "*** SUMMARY ***"
217 print "version: %s" % self["version"]
218 print "branch: %s" % self["merge_to_branch"] 190 print "branch: %s" % self["merge_to_branch"]
219 if self["revision_list"]: 191 if self["revision_list"]:
220 print "patches: %s" % self["revision_list"] 192 print "patches: %s" % self["revision_list"]
221 193
222 194
223 class MergeToBranch(ScriptsBase): 195 class MergeToBranch(ScriptsBase):
224 def _Description(self): 196 def _Description(self):
225 return ("Performs the necessary steps to merge revisions from " 197 return ("Performs the necessary steps to merge revisions from "
Michael Achenbach 2016/07/21 13:56:38 Suggestion: Add more details here including all re
Michael Hablich 2016/07/21 14:27:33 Done.
226 "master to other branches, including candidates.") 198 "master to release branches like 4.5.")
227 199
228 def _PrepareOptions(self, parser): 200 def _PrepareOptions(self, parser):
229 group = parser.add_mutually_exclusive_group(required=True) 201 group = parser.add_mutually_exclusive_group(required=True)
230 group.add_argument("--branch", help="The branch to merge to.") 202 group.add_argument("--branch", help="The branch to merge to.")
231 parser.add_argument("revisions", nargs="*", 203 parser.add_argument("revisions", nargs="*",
232 help="The revisions to merge.") 204 help="The revisions to merge.")
233 parser.add_argument("-f", "--force", 205 parser.add_argument("-f", "--force",
234 help="Delete sentinel file.", 206 help="Delete sentinel file.",
235 default=False, action="store_true") 207 default=False, action="store_true")
236 parser.add_argument("-m", "--message", 208 parser.add_argument("-m", "--message",
237 help="A commit message for the patch.") 209 help="A commit message for the patch.")
238 parser.add_argument("-p", "--patch", 210 parser.add_argument("-p", "--patch",
239 help="A patch file to apply as part of the merge.") 211 help="A patch file to apply as part of the merge.")
240 212
241 def _ProcessOptions(self, options): 213 def _ProcessOptions(self, options):
242 if len(options.revisions) < 1: 214 if len(options.revisions) < 1:
243 if not options.patch: 215 if not options.patch:
244 print "Either a patch file or revision numbers must be specified" 216 print "Either a patch file or revision numbers must be specified"
245 return False 217 return False
246 if not options.message: 218 if not options.message:
247 print "You must specify a merge comment if no patches are specified" 219 print "You must specify a merge comment if no patches are specified"
248 return False 220 return False
249 options.bypass_upload_hooks = True 221 options.bypass_upload_hooks = True
250 # CC ulan to make sure that fixes are merged to Google3. 222 # CC ulan to make sure that fixes are merged to Google3.
251 options.cc = "ulan@chromium.org" 223 options.cc = "ulan@chromium.org"
252 224
225 # This script only supports official release branches, not roll branches
Michael Achenbach 2016/07/21 13:56:37 nit: Comment is redundant to print message. Rather
Michael Hablich 2016/07/21 14:27:33 Done.
226 if len(options.branch.split('.')) > 2:
227 print ("This script does not support merging to roll branches (yet). "
Michael Achenbach 2016/07/21 13:56:37 nit: remove 'yet' to avoid embarrassment when it's
Michael Hablich 2016/07/21 14:27:32 Done.
228 "Please use tools/release/roll_merge.py for this use case.")
Michael Achenbach 2016/07/21 13:56:37 nit indentation - one space more...
Michael Hablich 2016/07/21 14:27:33 Done.
229 return False
230
253 # Make sure to use git hashes in the new workflows. 231 # Make sure to use git hashes in the new workflows.
254 for revision in options.revisions: 232 for revision in options.revisions:
255 if (IsSvnNumber(revision) or 233 if (IsSvnNumber(revision) or
256 (revision[0:1] == "r" and IsSvnNumber(revision[1:]))): 234 (revision[0:1] == "r" and IsSvnNumber(revision[1:]))):
257 print "Please provide full git hashes of the patches to merge." 235 print "Please provide full git hashes of the patches to merge."
258 print "Got: %s" % revision 236 print "Got: %s" % revision
259 return False 237 return False
260 return True 238 return True
261 239
262 def _Config(self): 240 def _Config(self):
263 return { 241 return {
264 "BRANCHNAME": "prepare-merge", 242 "BRANCHNAME": "prepare-merge",
265 "PERSISTFILE_BASENAME": "/tmp/v8-merge-to-branch-tempfile", 243 "PERSISTFILE_BASENAME": "/tmp/v8-merge-to-branch-tempfile",
266 "ALREADY_MERGING_SENTINEL_FILE": 244 "ALREADY_MERGING_SENTINEL_FILE":
267 "/tmp/v8-merge-to-branch-tempfile-already-merging", 245 "/tmp/v8-merge-to-branch-tempfile-already-merging",
268 "TEMPORARY_PATCH_FILE": "/tmp/v8-prepare-merge-tempfile-temporary-patch", 246 "TEMPORARY_PATCH_FILE": "/tmp/v8-prepare-merge-tempfile-temporary-patch",
269 "COMMITMSG_FILE": "/tmp/v8-prepare-merge-tempfile-commitmsg", 247 "COMMITMSG_FILE": "/tmp/v8-prepare-merge-tempfile-commitmsg",
270 } 248 }
271 249
272 def _Steps(self): 250 def _Steps(self):
273 return [ 251 return [
274 Preparation, 252 Preparation,
275 CreateBranch, 253 CreateBranch,
276 SearchArchitecturePorts, 254 SearchArchitecturePorts,
277 CreateCommitMessage, 255 CreateCommitMessage,
278 ApplyPatches, 256 ApplyPatches,
279 PrepareVersion,
280 IncrementVersion,
281 CommitLocal, 257 CommitLocal,
282 UploadStep, 258 UploadStep,
259 AddInformationalComment,
283 CommitRepository, 260 CommitRepository,
284 TagRevision,
285 CleanUp, 261 CleanUp,
286 ] 262 ]
287 263
288 264
289 if __name__ == "__main__": # pragma: no cover 265 if __name__ == "__main__": # pragma: no cover
290 sys.exit(MergeToBranch().Run()) 266 sys.exit(MergeToBranch().Run())
OLDNEW
« no previous file with comments | « tools/release/git_recipes.py ('k') | tools/release/roll_merge.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698