2009-12-06 Adam Barth <abarth@webkit.org>
[WebKit-https.git] / WebKitTools / Scripts / modules / landingsequence.py
1 #!/usr/bin/env python
2 # Copyright (c) 2009, Google Inc. All rights reserved.
3 # Copyright (c) 2009 Apple Inc. All rights reserved.
4 #
5 # Redistribution and use in source and binary forms, with or without
6 # modification, are permitted provided that the following conditions are
7 # met:
8
9 #     * Redistributions of source code must retain the above copyright
10 # notice, this list of conditions and the following disclaimer.
11 #     * Redistributions in binary form must reproduce the above
12 # copyright notice, this list of conditions and the following disclaimer
13 # in the documentation and/or other materials provided with the
14 # distribution.
15 #     * Neither the name of Google Inc. nor the names of its
16 # contributors may be used to endorse or promote products derived from
17 # this software without specific prior written permission.
18
19 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31 from modules.comments import bug_comment_from_commit_text
32 from modules.logging import log
33 from modules.scm import ScriptError, CheckoutNeedsUpdate
34 from modules.webkitport import WebKitPort
35 from modules.workqueue import WorkQueue
36
37 class LandingSequenceErrorHandler():
38     @classmethod
39     def handle_script_error(cls, tool, patch, script_error):
40         raise NotImplementedError, "subclasses must implement"
41
42
43 class LandingSequence:
44     def __init__(self, patch, options, tool):
45         self._patch = patch
46         self._options = options
47         self._tool = tool
48         self._port = WebKitPort.port(self._options.port)
49
50     def run(self):
51         self.clean()
52         self.update()
53         self.apply_patch()
54         self.check_builders()
55         self.build()
56         self.test()
57         commit_log = self.commit()
58         self.close_patch(commit_log)
59         self.close_bug()
60
61     def run_and_handle_errors(self):
62         try:
63             self.run()
64         except CheckoutNeedsUpdate, e:
65             log("Commit failed because the checkout is out of date.  Please update and try again.")
66             log("You can pass --no-build to skip building/testing after update if you believe the new commits did not affect the results.")
67             WorkQueue.exit_after_handled_error(e)
68         except ScriptError, e:
69             if not self._options.quiet:
70                 log(e.message_with_output())
71             if self._options.parent_command:
72                 command = self._tool.command_by_name(self._options.parent_command)
73                 command.handle_script_error(self._tool, self._patch, e)
74             WorkQueue.exit_after_handled_error(e)
75
76     def clean(self):
77         self._tool.steps.clean_working_directory(self._tool.scm(), self._options)
78
79     def update(self):
80         self._tool.steps.update(port=self._port)
81
82     def apply_patch(self):
83         log("Processing patch %s from bug %s." % (self._patch["id"], self._patch["bug_id"]))
84         self._tool.scm().apply_patch(self._patch, force=self._options.non_interactive)
85
86     def check_builders(self):
87         self._tool.steps.ensure_builders_are_green(self._tool.buildbot, self._options)
88
89     def build(self):
90         self._tool.steps.build_webkit(quiet=self._options.quiet, port=self._port)
91
92     def test(self):
93         # When running non-interactively we don't want to launch Safari and we want to exit after the first failure.
94         self._tool.steps.run_tests(launch_safari=not self._options.non_interactive, fail_fast=self._options.non_interactive, quiet=self._options.quiet, port=self._port)
95
96     def commit(self):
97         commit_message = self._tool.scm().commit_message_for_this_commit()
98         return self._tool.scm().commit_with_message(commit_message.message())
99
100     def close_patch(self, commit_log):
101         comment_text = bug_comment_from_commit_text(self._tool.scm(), commit_log)
102         self._tool.bugs.clear_attachment_flags(self._patch["id"], comment_text)
103
104     def close_bug(self):
105         # Check to make sure there are no r? or r+ patches on the bug before closing.
106         # Assume that r- patches are just previous patches someone forgot to obsolete.
107         patches = self._tool.bugs.fetch_patches_from_bug(self._patch["bug_id"])
108         for patch in patches:
109             review_flag = patch.get("review")
110             if review_flag == "?" or review_flag == "+":
111                 log("Not closing bug %s as attachment %s has review=%s.  Assuming there are more patches to land from this bug." % (patch["bug_id"], patch["id"], review_flag))
112                 return
113         self._tool.bugs.close_bug_as_fixed(self._patch["bug_id"], "All reviewed patches have been landed.  Closing bug.")
114
115
116 class ConditionalLandingSequence(LandingSequence):
117     def __init__(self, patch, options, tool):
118         LandingSequence.__init__(self, patch, options, tool)
119
120     def update(self):
121         if self._options.update:
122             LandingSequence.update(self)
123
124     def check_builders(self):
125         if self._options.build:
126             LandingSequence.check_builders(self)
127
128     def build(self):
129         if self._options.build:
130             LandingSequence.build(self)
131
132     def test(self):
133         if self._options.build and self._options.test:
134             LandingSequence.test(self)
135
136     def close_bug(self):
137         if self._options.close_bug:
138             LandingSequence.close_bug(self)
139