|
1 # Copyright (c) 2010 Google Inc. All rights reserved. |
|
2 # |
|
3 # Redistribution and use in source and binary forms, with or without |
|
4 # modification, are permitted provided that the following conditions are |
|
5 # met: |
|
6 # |
|
7 # * Redistributions of source code must retain the above copyright |
|
8 # notice, this list of conditions and the following disclaimer. |
|
9 # * Redistributions in binary form must reproduce the above |
|
10 # copyright notice, this list of conditions and the following disclaimer |
|
11 # in the documentation and/or other materials provided with the |
|
12 # distribution. |
|
13 # * Neither the name of Google Inc. nor the names of its |
|
14 # contributors may be used to endorse or promote products derived from |
|
15 # this software without specific prior written permission. |
|
16 # |
|
17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
18 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
19 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
20 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
21 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
22 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
23 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
24 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
28 |
|
29 from webkitpy.common.checkout.changelog import view_source_url |
|
30 from webkitpy.common.net.bugzilla import parse_bug_id |
|
31 from webkitpy.common.system.deprecated_logging import log |
|
32 from webkitpy.common.system.executive import ScriptError |
|
33 from webkitpy.tool.grammar import join_with_separators |
|
34 |
|
35 |
|
36 class Sheriff(object): |
|
37 def __init__(self, tool, sheriffbot): |
|
38 self._tool = tool |
|
39 self._sheriffbot = sheriffbot |
|
40 |
|
41 def post_irc_warning(self, commit_info, builders): |
|
42 irc_nicknames = sorted([party.irc_nickname for |
|
43 party in commit_info.responsible_parties() |
|
44 if party.irc_nickname]) |
|
45 irc_prefix = ": " if irc_nicknames else "" |
|
46 irc_message = "%s%s%s might have broken %s" % ( |
|
47 ", ".join(irc_nicknames), |
|
48 irc_prefix, |
|
49 view_source_url(commit_info.revision()), |
|
50 join_with_separators([builder.name() for builder in builders])) |
|
51 |
|
52 self._tool.irc().post(irc_message) |
|
53 |
|
54 def post_rollout_patch(self, svn_revision, rollout_reason): |
|
55 # Ensure that svn_revision is a number (and not an option to |
|
56 # create-rollout). |
|
57 try: |
|
58 svn_revision = int(svn_revision) |
|
59 except: |
|
60 raise ScriptError(message="Invalid svn revision number \"%s\"." |
|
61 % svn_revision) |
|
62 |
|
63 if rollout_reason.startswith("-"): |
|
64 raise ScriptError(message="The rollout reason may not begin " |
|
65 "with - (\"%s\")." % rollout_reason) |
|
66 |
|
67 output = self._sheriffbot.run_webkit_patch([ |
|
68 "create-rollout", |
|
69 "--force-clean", |
|
70 # In principle, we should pass --non-interactive here, but it |
|
71 # turns out that create-rollout doesn't need it yet. We can't |
|
72 # pass it prophylactically because we reject unrecognized command |
|
73 # line switches. |
|
74 "--parent-command=sheriff-bot", |
|
75 svn_revision, |
|
76 rollout_reason, |
|
77 ]) |
|
78 return parse_bug_id(output) |
|
79 |
|
80 def _rollout_reason(self, builders): |
|
81 # FIXME: This should explain which layout tests failed |
|
82 # however, that would require Build objects here, either passed |
|
83 # in through failure_info, or through Builder.latest_build. |
|
84 names = [builder.name() for builder in builders] |
|
85 return "Caused builders %s to fail." % join_with_separators(names) |
|
86 |
|
87 def post_automatic_rollout_patch(self, commit_info, builders): |
|
88 # For now we're only posting rollout patches for commit-queue patches. |
|
89 commit_bot_email = "eseidel@chromium.org" |
|
90 if commit_bot_email == commit_info.committer_email(): |
|
91 try: |
|
92 self.post_rollout_patch(commit_info.revision(), |
|
93 self._rollout_reason(builders)) |
|
94 except ScriptError, e: |
|
95 log("Failed to create-rollout.") |
|
96 |
|
97 def post_blame_comment_on_bug(self, commit_info, builders, blame_list): |
|
98 if not commit_info.bug_id(): |
|
99 return |
|
100 comment = "%s might have broken %s" % ( |
|
101 view_source_url(commit_info.revision()), |
|
102 join_with_separators([builder.name() for builder in builders])) |
|
103 if len(blame_list) > 1: |
|
104 comment += "\nThe following changes are on the blame list:\n" |
|
105 comment += "\n".join(map(view_source_url, blame_list)) |
|
106 self._tool.bugs.post_comment_to_bug(commit_info.bug_id(), |
|
107 comment, |
|
108 cc=self._sheriffbot.watchers) |
|
109 |
|
110 # FIXME: Should some of this logic be on BuildBot? |
|
111 def provoke_flaky_builders(self, revisions_causing_failures): |
|
112 # We force_build builders that are red but have not "failed" (i.e., |
|
113 # been red twice). We do this to avoid a deadlock situation where a |
|
114 # flaky test blocks the commit-queue and there aren't any other |
|
115 # patches being landed to re-spin the builder. |
|
116 failed_builders = sum([revisions_causing_failures[key] for |
|
117 key in revisions_causing_failures.keys()], []) |
|
118 failed_builder_names = \ |
|
119 set([builder.name() for builder in failed_builders]) |
|
120 idle_red_builder_names = \ |
|
121 set([builder["name"] |
|
122 for builder in self._tool.buildbot.idle_red_core_builders()]) |
|
123 |
|
124 # We only want to provoke these builders if they are idle and have not |
|
125 # yet "failed" (i.e., been red twice) to avoid overloading the bots. |
|
126 flaky_builder_names = idle_red_builder_names - failed_builder_names |
|
127 |
|
128 for name in flaky_builder_names: |
|
129 flaky_builder = self._tool.buildbot.builder_with_name(name) |
|
130 flaky_builder.force_build(username=self._sheriffbot.name, |
|
131 comments="Probe for flakiness.") |