OLD | NEW |
---|---|
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 |
(...skipping 158 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
169 def ReadLine(self): | 169 def ReadLine(self): |
170 return sys.stdin.readline().strip() | 170 return sys.stdin.readline().strip() |
171 | 171 |
172 DEFAULT_SIDE_EFFECT_HANDLER = SideEffectHandler() | 172 DEFAULT_SIDE_EFFECT_HANDLER = SideEffectHandler() |
173 | 173 |
174 | 174 |
175 class Step(object): | 175 class Step(object): |
176 def __init__(self, text="", requires=None): | 176 def __init__(self, text="", requires=None): |
177 self._text = text | 177 self._text = text |
178 self._number = -1 | 178 self._number = -1 |
179 self._options = None | |
179 self._requires = requires | 180 self._requires = requires |
180 self._side_effect_handler = DEFAULT_SIDE_EFFECT_HANDLER | 181 self._side_effect_handler = DEFAULT_SIDE_EFFECT_HANDLER |
181 | 182 |
182 def SetNumber(self, number): | 183 def SetNumber(self, number): |
183 self._number = number | 184 self._number = number |
184 | 185 |
185 def SetConfig(self, config): | 186 def SetConfig(self, config): |
186 self._config = config | 187 self._config = config |
187 | 188 |
188 def SetState(self, state): | 189 def SetState(self, state): |
(...skipping 16 matching lines...) Expand all Loading... | |
205 if self._requires: | 206 if self._requires: |
206 self.RestoreIfUnset(self._requires) | 207 self.RestoreIfUnset(self._requires) |
207 if not self._state[self._requires]: | 208 if not self._state[self._requires]: |
208 return | 209 return |
209 print ">>> Step %d: %s" % (self._number, self._text) | 210 print ">>> Step %d: %s" % (self._number, self._text) |
210 self.RunStep() | 211 self.RunStep() |
211 | 212 |
212 def RunStep(self): | 213 def RunStep(self): |
213 raise NotImplementedError | 214 raise NotImplementedError |
214 | 215 |
215 def ReadLine(self): | 216 def ReadLine(self, default=None): |
216 return self._side_effect_handler.ReadLine() | 217 # Don't prompt in forced mode. |
218 if self._options and self._options.f and default is not None: | |
219 print "%s (forced)" % default | |
220 return default | |
221 else: | |
222 return self._side_effect_handler.ReadLine() | |
217 | 223 |
218 def Git(self, args="", prefix="", pipe=True): | 224 def Git(self, args="", prefix="", pipe=True): |
219 return self._side_effect_handler.Command("git", args, prefix, pipe) | 225 return self._side_effect_handler.Command("git", args, prefix, pipe) |
220 | 226 |
221 def Editor(self, args): | 227 def Editor(self, args): |
222 return self._side_effect_handler.Command(os.environ["EDITOR"], args, | 228 return self._side_effect_handler.Command(os.environ["EDITOR"], args, |
223 pipe=False) | 229 pipe=False) |
224 | 230 |
225 def Die(self, msg=""): | 231 def Die(self, msg=""): |
226 if msg != "": | 232 if msg != "": |
227 print "Error: %s" % msg | 233 print "Error: %s" % msg |
228 print "Exiting" | 234 print "Exiting" |
229 raise Exception(msg) | 235 raise Exception(msg) |
230 | 236 |
237 def DieInFocedMode(self, msg=""): | |
Jakob Kummerow
2013/11/19 13:46:11
s/Foced/Forced/
Michael Achenbach
2013/11/19 15:09:37
Done.
| |
238 if self._options and self._options.f: | |
239 msg = msg or "Not implemented in forced mode." | |
240 self.Die(msg) | |
241 | |
231 def Confirm(self, msg): | 242 def Confirm(self, msg): |
232 print "%s [Y/n] " % msg, | 243 print "%s [Y/n] " % msg, |
233 answer = self.ReadLine() | 244 answer = self.ReadLine(default="Y") |
234 return answer == "" or answer == "Y" or answer == "y" | 245 return answer == "" or answer == "Y" or answer == "y" |
235 | 246 |
236 def DeleteBranch(self, name): | 247 def DeleteBranch(self, name): |
237 git_result = self.Git("branch").strip() | 248 git_result = self.Git("branch").strip() |
238 for line in git_result.splitlines(): | 249 for line in git_result.splitlines(): |
239 if re.match(r".*\s+%s$" % name, line): | 250 if re.match(r".*\s+%s$" % name, line): |
240 msg = "Branch %s exists, do you want to delete it?" % name | 251 msg = "Branch %s exists, do you want to delete it?" % name |
241 if self.Confirm(msg): | 252 if self.Confirm(msg): |
242 if self.Git("branch -D %s" % name) is None: | 253 if self.Git("branch -D %s" % name) is None: |
243 self.Die("Deleting branch '%s' failed." % name) | 254 self.Die("Deleting branch '%s' failed." % name) |
(...skipping 13 matching lines...) Expand all Loading... | |
257 | 268 |
258 def RestoreIfUnset(self, var_name): | 269 def RestoreIfUnset(self, var_name): |
259 if self._state.get(var_name) is None: | 270 if self._state.get(var_name) is None: |
260 self._state[var_name] = self.Restore(var_name) | 271 self._state[var_name] = self.Restore(var_name) |
261 | 272 |
262 def InitialEnvironmentChecks(self): | 273 def InitialEnvironmentChecks(self): |
263 # Cancel if this is not a git checkout. | 274 # Cancel if this is not a git checkout. |
264 if not os.path.exists(self._config[DOT_GIT_LOCATION]): | 275 if not os.path.exists(self._config[DOT_GIT_LOCATION]): |
265 self.Die("This is not a git checkout, this script won't work for you.") | 276 self.Die("This is not a git checkout, this script won't work for you.") |
266 | 277 |
278 # TODO(machenbach): Don't use EDITOR in forced mode as soon as script is | |
279 # well tested. | |
267 # Cancel if EDITOR is unset or not executable. | 280 # Cancel if EDITOR is unset or not executable. |
268 if (not os.environ.get("EDITOR") or | 281 if (not os.environ.get("EDITOR") or |
269 Command("which", os.environ["EDITOR"]) is None): | 282 Command("which", os.environ["EDITOR"]) is None): |
270 self.Die("Please set your EDITOR environment variable, you'll need it.") | 283 self.Die("Please set your EDITOR environment variable, you'll need it.") |
271 | 284 |
272 def CommonPrepare(self): | 285 def CommonPrepare(self): |
273 # Check for a clean workdir. | 286 # Check for a clean workdir. |
274 if self.Git("status -s -uno").strip() != "": | 287 if self.Git("status -s -uno").strip() != "": |
275 self.Die("Workspace is not clean. Please commit or undo your changes.") | 288 self.Die("Workspace is not clean. Please commit or undo your changes.") |
276 | 289 |
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
328 self.RestoreIfUnset("%s%s" % (prefix, v)) | 341 self.RestoreIfUnset("%s%s" % (prefix, v)) |
329 | 342 |
330 def WaitForLGTM(self): | 343 def WaitForLGTM(self): |
331 print ("Please wait for an LGTM, then type \"LGTM<Return>\" to commit " | 344 print ("Please wait for an LGTM, then type \"LGTM<Return>\" to commit " |
332 "your change. (If you need to iterate on the patch or double check " | 345 "your change. (If you need to iterate on the patch or double check " |
333 "that it's sane, do so in another shell, but remember to not " | 346 "that it's sane, do so in another shell, but remember to not " |
334 "change the headline of the uploaded CL.") | 347 "change the headline of the uploaded CL.") |
335 answer = "" | 348 answer = "" |
336 while answer != "LGTM": | 349 while answer != "LGTM": |
337 print "> ", | 350 print "> ", |
351 # TODO(machenbach): Add default="LGTM" to avoid prompt when script is | |
352 # well tested and when prepare push cl has TBR flag. | |
338 answer = self.ReadLine() | 353 answer = self.ReadLine() |
339 if answer != "LGTM": | 354 if answer != "LGTM": |
340 print "That was not 'LGTM'." | 355 print "That was not 'LGTM'." |
341 | 356 |
342 def WaitForResolvingConflicts(self, patch_file): | 357 def WaitForResolvingConflicts(self, patch_file): |
343 print("Applying the patch \"%s\" failed. Either type \"ABORT<Return>\", " | 358 print("Applying the patch \"%s\" failed. Either type \"ABORT<Return>\", " |
344 "or resolve the conflicts, stage *all* touched files with " | 359 "or resolve the conflicts, stage *all* touched files with " |
345 "'git add', and type \"RESOLVED<Return>\"") | 360 "'git add', and type \"RESOLVED<Return>\"") |
361 self.DieInFocedMode() | |
346 answer = "" | 362 answer = "" |
347 while answer != "RESOLVED": | 363 while answer != "RESOLVED": |
348 if answer == "ABORT": | 364 if answer == "ABORT": |
349 self.Die("Applying the patch failed.") | 365 self.Die("Applying the patch failed.") |
350 if answer != "": | 366 if answer != "": |
351 print "That was not 'RESOLVED' or 'ABORT'." | 367 print "That was not 'RESOLVED' or 'ABORT'." |
352 print "> ", | 368 print "> ", |
353 answer = self.ReadLine() | 369 answer = self.ReadLine() |
354 | 370 |
355 # Takes a file containing the patch to apply as first argument. | 371 # Takes a file containing the patch to apply as first argument. |
356 def ApplyPatch(self, patch_file, reverse_patch=""): | 372 def ApplyPatch(self, patch_file, reverse_patch=""): |
357 args = "apply --index --reject %s \"%s\"" % (reverse_patch, patch_file) | 373 args = "apply --index --reject %s \"%s\"" % (reverse_patch, patch_file) |
358 if self.Git(args) is None: | 374 if self.Git(args) is None: |
359 self.WaitForResolvingConflicts(patch_file) | 375 self.WaitForResolvingConflicts(patch_file) |
360 | 376 |
361 | 377 |
362 class UploadStep(Step): | 378 class UploadStep(Step): |
363 def __init__(self): | 379 def __init__(self): |
364 Step.__init__(self, "Upload for code review.") | 380 Step.__init__(self, "Upload for code review.") |
365 | 381 |
366 def RunStep(self): | 382 def RunStep(self): |
367 print "Please enter the email address of a V8 reviewer for your patch: ", | 383 if self._options and self._options.r: |
368 reviewer = self.ReadLine() | 384 print "Using account %s for review." % self._options.r |
385 reviewer = self._options.r | |
386 else: | |
387 print "Please enter the email address of a V8 reviewer for your patch: ", | |
388 self.DieInFocedMode("A reviewer must be specified in forced mode.") | |
389 reviewer = self.ReadLine() | |
369 args = "cl upload -r \"%s\" --send-mail" % reviewer | 390 args = "cl upload -r \"%s\" --send-mail" % reviewer |
370 if self.Git(args,pipe=False) is None: | 391 if self.Git(args,pipe=False) is None: |
371 self.Die("'git cl upload' failed, please try again.") | 392 self.Die("'git cl upload' failed, please try again.") |
372 | 393 |
373 | 394 |
374 def RunScript(step_classes, | 395 def RunScript(step_classes, |
375 config, | 396 config, |
376 options, | 397 options, |
377 side_effect_handler=DEFAULT_SIDE_EFFECT_HANDLER): | 398 side_effect_handler=DEFAULT_SIDE_EFFECT_HANDLER): |
378 state = {} | 399 state = {} |
379 steps = [] | 400 steps = [] |
380 number = 0 | 401 number = 0 |
381 | 402 |
382 for step_class in step_classes: | 403 for step_class in step_classes: |
383 # TODO(machenbach): Factory methods. | 404 # TODO(machenbach): Factory methods. |
384 step = step_class() | 405 step = step_class() |
385 step.SetNumber(number) | 406 step.SetNumber(number) |
386 step.SetConfig(config) | 407 step.SetConfig(config) |
387 step.SetOptions(options) | 408 step.SetOptions(options) |
388 step.SetState(state) | 409 step.SetState(state) |
389 step.SetSideEffectHandler(side_effect_handler) | 410 step.SetSideEffectHandler(side_effect_handler) |
390 steps.append(step) | 411 steps.append(step) |
391 number += 1 | 412 number += 1 |
392 | 413 |
393 for step in steps[options.s:]: | 414 for step in steps[options.s:]: |
394 step.Run() | 415 step.Run() |
OLD | NEW |