Changeset 52480 in webkit


Ignore:
Timestamp:
Dec 21, 2009 10:46:57 PM (14 years ago)
Author:
abarth@webkit.org
Message:

2009-12-21 Adam Barth <abarth@webkit.org>

Reviewed by Eric Seidel.

[bzt] Automate the process of calling prepare-ChangeLog
https://bugs.webkit.org/show_bug.cgi?id=32816

This patch automates the process of creating a bug and patch and
uploading it to bugzilla. The first cut just calls
prepare-ChangeLog.

This patch required some refactoring of upload.py to the Step
model, but that's worth doing anyway.

  • Scripts/bugzilla-tool:
  • Scripts/modules/buildsteps.py:
  • Scripts/modules/commands/download.py:
  • Scripts/modules/commands/upload.py:
  • Scripts/modules/commands/upload_unittest.py:
  • Scripts/modules/mock_bugzillatool.py:
Location:
trunk/WebKitTools
Files:
7 edited
1 copied

Legend:

Unmodified
Added
Removed
  • trunk/WebKitTools/ChangeLog

    r52457 r52480  
     12009-12-21  Adam Barth  <abarth@webkit.org>
     2
     3        Reviewed by Eric Seidel.
     4
     5        [bzt] Automate the process of calling prepare-ChangeLog
     6        https://bugs.webkit.org/show_bug.cgi?id=32816
     7
     8        This patch automates the process of creating a bug and patch and
     9        uploading it to bugzilla.  The first cut just calls
     10        prepare-ChangeLog.
     11
     12        This patch required some refactoring of upload.py to the Step
     13        model, but that's worth doing anyway.
     14
     15        * Scripts/bugzilla-tool:
     16        * Scripts/modules/buildsteps.py:
     17        * Scripts/modules/commands/download.py:
     18        * Scripts/modules/commands/upload.py:
     19        * Scripts/modules/commands/upload_unittest.py:
     20        * Scripts/modules/mock_bugzillatool.py:
     21
    1222009-12-21  Darin Adler  <darin@apple.com>
    223
  • trunk/WebKitTools/Scripts/bugzilla-tool

    r52145 r52480  
    4444from modules.multicommandtool import MultiCommandTool
    4545from modules.scm import detect_scm_system
     46from modules.user import User
     47
    4648
    4749class BugzillaTool(MultiCommandTool):
     
    5456        self.buildbot = BuildBot()
    5557        self.executive = Executive()
     58        self.user = User()
    5659        self._scm = None
    5760        self.status_bot = StatusBot()
  • trunk/WebKitTools/Scripts/modules/buildsteps.py

    r52241 r52480  
    2828
    2929import os
     30import StringIO
    3031
    3132from optparse import make_option
     
    5354    reviewer = make_option("-r", "--reviewer", action="store", type="string", dest="reviewer", help="Update ChangeLogs to say Reviewed by REVIEWER.")
    5455    complete_rollout = make_option("--complete-rollout", action="store_true", dest="complete_rollout", help="Commit the revert and re-open the original bug.")
     56    obsolete_patches = make_option("--no-obsolete", action="store_false", dest="obsolete_patches", default=True, help="Do not obsolete old patches before posting this one.")
     57    review = make_option("--no-review", action="store_false", dest="review", default=True, help="Do not mark the patch for review.")
     58    request_commit = make_option("--request-commit", action="store_true", dest="request_commit", default=False, help="Mark the patch as needing auto-commit after review.")
     59    description = make_option("-m", "--description", action="store", type="string", dest="description", help="Description string for the attachment (default: \"patch\")")
    5560
    5661
     
    6368    def _run_script(self, script_name, quiet=False, port=WebKitPort):
    6469        log("Running %s" % script_name)
     70        # FIXME: This should use self.port()
    6571        self._tool.executive.run_and_throw_if_fail(port.script_path(script_name), quiet)
    6672
     
    106112
    107113class PrepareChangelogStep(AbstractStep):
    108     def run(self, state):
    109         self._run_script("prepare-ChangeLog")
     114    @classmethod
     115    def options(cls):
     116        return [
     117            CommandOptions.port,
     118            CommandOptions.quiet,
     119            CommandOptions.non_interactive,
     120        ]
     121
     122    def run(self, state):
     123        os.chdir(self._tool.scm().checkout_root)
     124        args = [self.port().script_path("prepare-ChangeLog")]
     125        if not self._options.non_interactive:
     126            args.append("-o")
     127        if state["bug_id"]:
     128            args.append("--bug=%s" % state["bug_id"])
     129        self._tool.executive.run_and_throw_if_fail(args, self._options.quiet)
     130        if not self._options.non_interactive:
     131            self._tool.user.prompt("Press enter when ready to continue.")
     132
     133
     134class ObsoletePatchesOnBugStep(AbstractStep):
     135    @classmethod
     136    def options(cls):
     137        return [
     138            CommandOptions.obsolete_patches,
     139        ]
     140
     141    def run(self, state):
     142        if not self._options.obsolete_patches:
     143            return
     144        bug_id = state["bug_id"]
     145        patches = self._tool.bugs.fetch_patches_from_bug(bug_id)
     146        if not patches:
     147            return
     148        log("Obsoleting %s on bug %s" % (pluralize("old patch", len(patches)), bug_id))
     149        for patch in patches:
     150            self._tool.bugs.obsolete_attachment(patch["id"])
     151
     152
     153class PostDiffToBugStep(AbstractStep):
     154    @classmethod
     155    def options(cls):
     156        return [
     157            CommandOptions.description,
     158            CommandOptions.review,
     159            CommandOptions.request_commit,
     160        ]
     161
     162    def run(self, state):
     163        diff = self._tool.scm().create_patch()
     164        diff_file = StringIO.StringIO(diff) # add_patch_to_bug expects a file-like object
     165        description = self._options.description or "Patch"
     166        self._tool.bugs.add_patch_to_bug(state["bug_id"], diff_file, description, mark_for_review=self._options.review, mark_for_commit_queue=self._options.request_commit)
    110167
    111168
  • trunk/WebKitTools/Scripts/modules/commands/download.py

    r52295 r52480  
    5353
    5454
     55# FIXME: Move this to a more general location.
    5556class AbstractSequencedCommmand(AbstractDeclarativeCommmand):
    5657    steps = None
  • trunk/WebKitTools/Scripts/modules/commands/upload.py

    r52295 r52480  
    3737
    3838from modules.bugzilla import parse_bug_id
     39from modules.buildsteps import PrepareChangelogStep, CommandOptions, ObsoletePatchesOnBugStep, PostDiffToBugStep
     40from modules.commands.download import AbstractSequencedCommmand
    3941from modules.comments import bug_comment_from_svn_revision
    4042from modules.grammar import pluralize
     
    5355
    5456
    55 class ObsoleteAttachments(Command):
     57class ObsoleteAttachments(AbstractSequencedCommmand):
    5658    name = "obsolete-attachments"
    57     def __init__(self):
    58         Command.__init__(self, "Mark all attachments on a bug as obsolete", "BUGID")
    59 
    60     def execute(self, options, args, tool):
    61         bug_id = args[0]
    62         attachments = tool.bugs.fetch_attachments_from_bug(bug_id)
    63         for attachment in attachments:
    64             if not attachment["is_obsolete"]:
    65                 tool.bugs.obsolete_attachment(attachment["id"])
    66 
    67 
    68 class PostDiff(Command):
     59    help_text = "Mark all attachments on a bug as obsolete"
     60    argument_names = "BUGID"
     61    steps = [
     62        ObsoletePatchesOnBugStep,
     63    ]
     64
     65    def _prepare_state(self, options, args, tool):
     66        return { "bug_id" : args[0] }
     67
     68
     69class PostDiff(AbstractSequencedCommmand):
    6970    name = "post-diff"
     71    help_text = "Attach the current working directory diff to a bug as a patch file"
     72    argument_names = "[BUGID]"
    7073    show_in_main_help = True
    71     def __init__(self):
    72         options = [
    73             make_option("-m", "--description", action="store", type="string", dest="description", help="Description string for the attachment (default: \"patch\")"),
    74         ]
    75         options += self.posting_options()
    76         Command.__init__(self, "Attach the current working directory diff to a bug as a patch file", "[BUGID]", options=options)
    77 
    78     @staticmethod
    79     def posting_options():
    80         return [
    81             make_option("--no-obsolete", action="store_false", dest="obsolete_patches", default=True, help="Do not obsolete old patches before posting this one."),
    82             make_option("--no-review", action="store_false", dest="review", default=True, help="Do not mark the patch for review."),
    83             make_option("--request-commit", action="store_true", dest="request_commit", default=False, help="Mark the patch as needing auto-commit after review."),
    84         ]
    85 
    86     @staticmethod
    87     def obsolete_patches_on_bug(bug_id, bugs):
    88         patches = bugs.fetch_patches_from_bug(bug_id)
    89         if len(patches):
    90             log("Obsoleting %s on bug %s" % (pluralize("old patch", len(patches)), bug_id))
    91             for patch in patches:
    92                 bugs.obsolete_attachment(patch["id"])
    93 
    94     def execute(self, options, args, tool):
     74    steps = [
     75        ObsoletePatchesOnBugStep,
     76        PostDiffToBugStep,
     77    ]
     78
     79    def _prepare_state(self, options, args, tool):
    9580        # Perfer a bug id passed as an argument over a bug url in the diff (i.e. ChangeLogs).
    9681        bug_id = (args and args[0]) or parse_bug_id(tool.scm().create_patch())
    9782        if not bug_id:
    9883            error("No bug id passed and no bug url found in diff, can't post.")
    99 
    100         if options.obsolete_patches:
    101             self.obsolete_patches_on_bug(bug_id, tool.bugs)
    102 
    103         diff = tool.scm().create_patch()
    104         diff_file = StringIO.StringIO(diff) # add_patch_to_bug expects a file-like object
    105 
    106         description = options.description or "Patch"
    107         tool.bugs.add_patch_to_bug(bug_id, diff_file, description, mark_for_review=options.review, mark_for_commit_queue=options.request_commit)
     84        return { "bug_id" : bug_id }
     85
     86
     87class SubmitPatch(AbstractSequencedCommmand):
     88    name = "submit-patch"
     89    help_text = "Experimental.  Creates a patch from the current working copy and uploads bugzilla"
     90    argument_names = "BUGID"
     91    steps = [
     92        PrepareChangelogStep,
     93        # FIXME: Add a CreateBugStep!
     94        ObsoletePatchesOnBugStep,
     95        PostDiffToBugStep,
     96    ]
     97
     98    def _prepare_state(self, options, args, tool):
     99        bug_id = args[0]
     100        return { "bug_id" : bug_id }
    108101
    109102
     
    116109            make_option("--add-log-as-comment", action="store_true", dest="add_log_as_comment", default=False, help="Add commit log message as a comment when uploading the patch."),
    117110            make_option("-m", "--description", action="store", type="string", dest="description", help="Description string for the attachment (default: description from commit message)"),
     111            CommandOptions.obsolete_patches,
     112            CommandOptions.review,
     113            CommandOptions.request_commit,
    118114        ]
    119         options += PostDiff.posting_options()
    120115        Command.__init__(self, "Attach a range of local commits to bugs as patch files", "COMMITISH", options=options, requires_local_commits=True)
    121116
     
    148143
    149144            if options.obsolete_patches and bug_id not in have_obsoleted_patches:
    150                 PostDiff.obsolete_patches_on_bug(bug_id, tool.bugs)
     145                state = { "bug_id": bug_id }
     146                ObsoletePatchesOnBugStep(tool, options).run(state)
    151147                have_obsoleted_patches.add(bug_id)
    152148
  • trunk/WebKitTools/Scripts/modules/commands/upload_unittest.py

    r52148 r52480  
    3434class UploadCommandsTest(CommandsTest):
    3535    def test_obsolete_attachments(self):
    36         self.assert_execute_outputs(ObsoleteAttachments(), [42])
     36        expected_stderr = "Obsoleting 2 old patches on bug 42\n"
     37        self.assert_execute_outputs(ObsoleteAttachments(), [42], expected_stderr=expected_stderr)
    3738
    3839    def test_post_diff(self):
    3940        expected_stderr = "Obsoleting 2 old patches on bug 42\n"
    4041        self.assert_execute_outputs(PostDiff(), [42], expected_stderr=expected_stderr)
     42
     43    def test_submit_patch(self):
     44        expected_stderr = "Obsoleting 2 old patches on bug 42\n"
     45        self.assert_execute_outputs(SubmitPatch(), [42], expected_stderr=expected_stderr)
  • trunk/WebKitTools/Scripts/modules/mock_bugzillatool.py

    r52430 r52480  
    109109        return []
    110110
     111
    111112class MockSCM(Mock):
    112113    def __init__(self):
     
    143144
    144145
     146class MockUser(object):
     147    def prompt(self, message):
     148        return "Mock user response"
     149
     150
    145151class MockStatusBot(object):
    146152    def __init__(self):
     
    159165        self.buildbot = MockBuildBot()
    160166        self.executive = Mock()
     167        self.user = MockUser()
    161168        self._scm = MockSCM()
    162169        self.status_bot = MockStatusBot()
  • trunk/WebKitTools/Scripts/modules/user.py

    r52479 r52480  
    1 # Copyright (C) 2009 Google Inc. All rights reserved.
    2 #
     1# Copyright (c) 2009, Google Inc. All rights reserved.
     2# 
    33# Redistribution and use in source and binary forms, with or without
    44# modification, are permitted provided that the following conditions are
    55# met:
    6 #
    7 #    * Redistributions of source code must retain the above copyright
     6# 
     7#     * Redistributions of source code must retain the above copyright
    88# 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
    1010# copyright notice, this list of conditions and the following disclaimer
    1111# in the documentation and/or other materials provided with the
    1212# 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
    1414# contributors may be used to endorse or promote products derived from
    1515# this software without specific prior written permission.
    16 #
     16# 
    1717# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    1818# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     
    2727# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    2828
    29 import unittest
    30 
    31 from modules.commands.commandtest import CommandsTest
    32 from modules.commands.upload import *
    33 
    34 class UploadCommandsTest(CommandsTest):
    35     def test_obsolete_attachments(self):
    36         self.assert_execute_outputs(ObsoleteAttachments(), [42])
    37 
    38     def test_post_diff(self):
    39         expected_stderr = "Obsoleting 2 old patches on bug 42\n"
    40         self.assert_execute_outputs(PostDiff(), [42], expected_stderr=expected_stderr)
     29class User(object):
     30    def prompt(self, message):
     31        return raw_input(message)
Note: See TracChangeset for help on using the changeset viewer.