diff options
Diffstat (limited to 'WebKitTools/Scripts/bugzilla-tool')
-rwxr-xr-x | WebKitTools/Scripts/bugzilla-tool | 122 |
1 files changed, 72 insertions, 50 deletions
diff --git a/WebKitTools/Scripts/bugzilla-tool b/WebKitTools/Scripts/bugzilla-tool index ec5aa0d..8e899b5 100755 --- a/WebKitTools/Scripts/bugzilla-tool +++ b/WebKitTools/Scripts/bugzilla-tool @@ -73,7 +73,7 @@ def commit_message_for_this_commit(scm): log("Parsing ChangeLog: %s" % changelog_path) changelog_entry = ChangeLog(changelog_path).latest_entry() if not changelog_entry: - error("Failed to parse ChangeLog: " + os.path.abspath(changelog_path)) + raise ScriptError(message="Failed to parse ChangeLog: " + os.path.abspath(changelog_path)) changelog_messages.append(changelog_entry) # FIXME: We should sort and label the ChangeLog messages like commit-log-editor does. @@ -325,6 +325,11 @@ class LandPatchesFromBugs(Command): options += WebKitLandingScripts.land_options() Command.__init__(self, 'Lands all patches on a bug optionally testing them first', 'BUGID', options=options) + @staticmethod + def handled_error(error): + log(error) + exit(2) # Exit 2 insted of 1 to indicate to the commit-queue to indicate we handled the error, and that the queue should keep looping. + @classmethod def land_patches(cls, bug_id, patches, options, tool): try: @@ -344,11 +349,11 @@ class LandPatchesFromBugs(Command): except CheckoutNeedsUpdate, e: log("Commit was rejected because the checkout is out of date. Please update and try again.") log("You can pass --no-build to skip building/testing after update if you believe the new commits did not affect the results.") - error(e) + cls.handled_error(e) except ScriptError, e: # Mark the patch as commit-queue- and comment in the bug. tool.bugs.reject_patch_from_commit_queue(patch['id'], e.message_with_output()) - error(e) + cls.handled_error(e) @staticmethod def _fetch_list_of_patches_to_land(options, args, tool): @@ -420,6 +425,7 @@ class PostDiffAsPatchToBug(Command): return [ make_option("--no-obsolete", action="store_false", dest="obsolete_patches", default=True, help="Do not obsolete old patches before posting this one."), make_option("--no-review", action="store_false", dest="review", default=True, help="Do not mark the patch for review."), + make_option("--request-commit", action="store_true", dest="request_commit", default=False, help="Mark the patch as needing auto-commit after review."), ] @staticmethod @@ -443,7 +449,7 @@ class PostDiffAsPatchToBug(Command): diff_file = StringIO.StringIO(diff) # add_patch_to_bug expects a file-like object description = options.description or "Patch v1" - tool.bugs.add_patch_to_bug(bug_id, diff_file, description, mark_for_review=options.review) + tool.bugs.add_patch_to_bug(bug_id, diff_file, description, mark_for_review=options.review, mark_for_commit_queue=options.request_commit) class PostCommitsAsPatchesToBug(Command): @@ -493,7 +499,7 @@ class PostCommitsAsPatchesToBug(Command): diff_file = self._diff_file_for_commit(tool, commit_id) description = options.description or commit_message.description(lstrip=True, strip_url=True) comment_text = self._comment_text_for_commit(options, commit_message, tool, commit_id) - tool.bugs.add_patch_to_bug(bug_id, diff_file, description, comment_text, mark_for_review=options.review) + tool.bugs.add_patch_to_bug(bug_id, diff_file, description, comment_text, mark_for_review=options.review, mark_for_commit_queue=options.request_commit) class RolloutCommit(Command): @@ -560,6 +566,7 @@ class CreateBug(Command): make_option("--component", action="store", type="string", dest="component", help="Component for the new bug."), make_option("--no-prompt", action="store_false", dest="prompt", default=True, help="Do not prompt for bug title and comment; use commit log instead."), make_option("--no-review", action="store_false", dest="review", default=True, help="Do not mark the patch for review."), + make_option("--request-commit", action="store_true", dest="request_commit", default=False, help="Mark the patch as needing auto-commit after review."), ] Command.__init__(self, 'Create a bug from local changes or local commits.', '[COMMITISH]', options=options) @@ -583,7 +590,7 @@ class CreateBug(Command): diff = tool.scm().create_patch_from_local_commit(commit_id) diff_file = StringIO.StringIO(diff) # create_bug_with_patch expects a file-like object - bug_id = tool.bugs.create_bug_with_patch(bug_title, comment_text, options.component, diff_file, "Patch v1", cc=options.cc, mark_for_review=options.review) + bug_id = tool.bugs.create_bug_with_patch(bug_title, comment_text, options.component, diff_file, "Patch v1", cc=options.cc, mark_for_review=options.review, mark_for_commit_queue=options.request_commit) if bug_id and len(commit_ids) > 1: options.bug_id = bug_id @@ -603,7 +610,7 @@ class CreateBug(Command): diff = tool.scm().create_patch() diff_file = StringIO.StringIO(diff) # create_bug_with_patch expects a file-like object - bug_id = tool.bugs.create_bug_with_patch(bug_title, comment_text, options.component, diff_file, "Patch v1", cc=options.cc, mark_for_review=options.review) + bug_id = tool.bugs.create_bug_with_patch(bug_title, comment_text, options.component, diff_file, "Patch v1", cc=options.cc, mark_for_review=options.review, mark_for_commit_queue=options.request_commit) def prompt_for_bug_title_and_comment(self): bug_title = raw_input("Bug title: ") @@ -644,6 +651,7 @@ class CheckTreeStatus(Command): class LandPatchesFromCommitQueue(Command): def __init__(self): options = [ + make_option("--is-relaunch", action="store_true", dest="is_relaunch", default=False, help="Internal: Used by the queue to indicate that it's relaunching itself."), make_option("--no-confirm", action="store_false", dest="confirm", default=True, help="Do not ask the user for confirmation before running the queue. Dangerous!"), make_option("--status-host", action="store", type="string", dest="status_host", default=StatusBot.default_host, help="Do not ask the user for confirmation before running the queue. Dangerous!"), ] @@ -675,16 +683,25 @@ class LandPatchesFromCommitQueue(Command): wake_time = datetime.now() + timedelta(seconds=cls.seconds_to_sleep) return "%s Sleeping until %s (%s)." % (message, wake_time.strftime(cls.log_date_format), cls.sleep_duration_text) - @classmethod - def _sleep(cls, message): - log(cls._sleep_message(message)) - time.sleep(cls.seconds_to_sleep) + def _sleep(self, message): + log(self._sleep_message(message)) + time.sleep(self.seconds_to_sleep) + self._next_patch() def _update_status_and_sleep(self, message): status_message = self._sleep_message(message) self.status_bot.update_status(status_message) log(status_message) time.sleep(self.seconds_to_sleep) + self._next_patch() + + def _next_patch(self): + # Re-exec this script to catch any updates to the script. + # Make sure that the re-execed commit-queue does not wait for the user. + args = sys.argv[:] + if args.count("--is-relaunch") == 0: + args.append("--is-relaunch") + os.execvp(sys.argv[0], args) @staticmethod def _open_log_file(log_path): @@ -705,50 +722,55 @@ class LandPatchesFromCommitQueue(Command): log_file.close() def execute(self, options, args, tool): - log("CAUTION: commit-queue will discard all local changes in %s" % tool.scm().checkout_root) - if options.confirm: - response = raw_input("Are you sure? Type 'yes' to continue: ") - if (response != 'yes'): - error("User declined.") + if not options.is_relaunch: + log("CAUTION: commit-queue will discard all local changes in %s" % tool.scm().checkout_root) + if options.confirm: + response = raw_input("Are you sure? Type 'yes' to continue: ") + if (response != 'yes'): + error("User declined.") queue_log = self._add_log_to_output_tee(self.queue_log_path) - log("Running WebKit Commit Queue. %s" % datetime.now().strftime(self.log_date_format)) + if not options.is_relaunch: + log("Running WebKit Commit Queue. %s" % datetime.now().strftime(self.log_date_format)) self.status_bot = StatusBot(host=options.status_host) - while (True): - # Either of these calls could throw URLError which shouldn't stop the queue. - # We catch all exceptions just in case. - try: - # Fetch patches instead of just bug ids to that we validate reviewer/committer flags on every patch. - patches = tool.bugs.fetch_patches_from_commit_queue(reject_invalid_patches=True) - if not len(patches): - self._update_status_and_sleep("Empty queue.") - continue - patch_ids = map(lambda patch: patch['id'], patches) - first_bug_id = patches[0]['bug_id'] - log("%s in commit queue [%s]" % (pluralize('patch', len(patches)), ", ".join(patch_ids))) - - if not tool.buildbot.core_builders_are_green(): - self._update_status_and_sleep("Builders (http://build.webkit.org) are red.") - continue - - self.status_bot.update_status("Landing patches from bug %s." % first_bug_id, bug_id=first_bug_id) - except Exception, e: - # Don't try tell the status bot, in case telling it causes an exception. - self._sleep("Exception while checking queue and bots: %s." % e) - continue - - # Try to land patches on the first bug in the queue before looping - bug_log_path = os.path.join(self.bug_logs_directory, "%s.log" % first_bug_id) - bug_log = self._add_log_to_output_tee(bug_log_path) - bugzilla_tool_path = __file__ # re-execute this script - bugzilla_tool_args = [bugzilla_tool_path, 'land-patches', '--force-clean', '--commit-queue', '--quiet', first_bug_id] - WebKitLandingScripts.run_command_with_teed_output(bugzilla_tool_args, sys.stdout) - self._remove_log_from_output_tee(bug_log) - - log("Finished WebKit Commit Queue. %s" % datetime.now().strftime(self.log_date_format)) - self._remove_log_from_output_tee(queue_log) + # Either of these calls could throw URLError which shouldn't stop the queue. + # We catch all exceptions just in case. + try: + # Fetch patches instead of just bug ids to that we validate reviewer/committer flags on every patch. + patches = tool.bugs.fetch_patches_from_commit_queue(reject_invalid_patches=True) + if not len(patches): + self._update_status_and_sleep("Empty queue.") + patch_ids = map(lambda patch: patch['id'], patches) + first_bug_id = patches[0]['bug_id'] + log("%s in commit queue [%s]" % (pluralize('patch', len(patches)), ", ".join(patch_ids))) + + red_builders_names = tool.buildbot.red_core_builders_names() + if red_builders_names: + red_builders_names = map(lambda name: '"%s"' % name, red_builders_names) # Add quotes around the names. + self._update_status_and_sleep("Builders [%s] are red. See http://build.webkit.org." % ", ".join(red_builders_names)) + + self.status_bot.update_status("Landing patches from bug %s." % first_bug_id, bug_id=first_bug_id) + except Exception, e: + # Don't try tell the status bot, in case telling it causes an exception. + self._sleep("Exception while checking queue and bots: %s." % e) + + # Try to land patches on the first bug in the queue before looping + bug_log_path = os.path.join(self.bug_logs_directory, "%s.log" % first_bug_id) + bug_log = self._add_log_to_output_tee(bug_log_path) + bugzilla_tool_path = __file__ # re-execute this script + bugzilla_tool_args = [bugzilla_tool_path, 'land-patches', '--force-clean', '--commit-queue', '--quiet', first_bug_id] + try: + WebKitLandingScripts.run_and_throw_if_fail(bugzilla_tool_args) + except ScriptError, e: + # Unexpected failure! Mark the patch as commit-queue- and comment in the bug. + # exit(2) is a special exit code we use to indicate that the error was already handled by land-patches and we should keep looping anyway. + if e.exit_code != 2: + tool.bugs.reject_patch_from_commit_queue(patch['id'], "Unexpected failure when landing patch! Please file a bug against bugzilla-tool.\n%s" % e.message_with_output()) + self._remove_log_from_output_tee(bug_log) + # self._remove_log_from_output_tee(queue_log) # implicit in the exec() + self._next_patch() class NonWrappingEpilogIndentedHelpFormatter(IndentedHelpFormatter): |