• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mozilla / relman-auto-nag / #5331

07 Nov 2024 10:37PM CUT coverage: 21.301% (-0.09%) from 21.392%
#5331

push

coveralls-python

gmierz
Add needinfo to resolved regressions with no explicit reason.

426 of 2938 branches covered (14.5%)

0 of 50 new or added lines in 1 file covered. (0.0%)

4 existing lines in 1 file now uncovered.

1942 of 9117 relevant lines covered (21.3%)

0.21 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/bugbot/rules/perfalert_resolved_regression.py
1
# This Source Code Form is subject to the terms of the Mozilla Public
2
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
3
# You can obtain one at http://mozilla.org/MPL/2.0/.
4

NEW
5
from libmozdata import utils as lmdutils
×
NEW
6
from libmozdata.bugzilla import Bugzilla, BugzillaUser
×
7

8
from bugbot.bzcleaner import BzCleaner
×
NEW
9
from bugbot.constants import BOT_MAIN_ACCOUNT
×
10

NEW
11
DEFAULT_RESOLUTION_COMMENT = "No resolution comment provided."
×
12

13

NEW
14
class PerfAlertResolvedRegression(BzCleaner):
×
15
    def __init__(self, *args, **kwargs):
×
16
        super().__init__(*args, **kwargs)
×
17
        self.extra_email_info = {}
×
NEW
18
        self.extra_ni = {}
×
19
        self._bug_history = {}
×
20
        self._bug_comments = {}
×
21

22
    def description(self):
×
23
        return "PerfAlert regressions whose resolution has changed recently"
×
24

25
    def get_bz_params(self, date):
×
26
        fields = [
×
27
            "id",
28
            "resolution",
29
        ]
30

31
        # Find all bugs that have perf-alert, and regression in their keywords. Search
32
        # for bugs that have been changed in the last day. Only look for bugs after
33
        # October 1st, 2024 to prevent triggering comments on older performance regressions
34
        params = {
×
35
            "include_fields": fields,
36
            "f3": "creation_ts",
37
            "o3": "greaterthan",
38
            "v3": "2024-10-01T00:00:00Z",
39
            "f1": "regressed_by",
40
            "o1": "isnotempty",
41
            "f2": "keywords",
42
            "o2": "allwords",
43
            "v2": ["regression", "perf-alert"],
44
            "f4": "resolution",
45
            "o4": "changedafter",
46
            "v4": date,
47
        }
48

49
        return params
×
50

51
    def get_extra_for_template(self):
×
52
        return self.extra_email_info
×
53

NEW
54
    def get_extra_for_needinfo_template(self):
×
NEW
55
        return self.extra_ni
×
56

UNCOV
57
    def get_resolution_comments(self, bugs):
×
58
        # Match all the resolutions with resolution comments if they exist
59
        for bug_id, bug in bugs.items():
×
60
            bug_comments = self._bug_comments.get(bug_id, [])
×
61
            bug_history = self._bug_history.get(bug_id, {})
×
62

63
            # Sometimes a resolution comment is not provided so use a default
NEW
64
            bug_history["resolution_comment"] = DEFAULT_RESOLUTION_COMMENT
×
65
            for comment in bug_comments[::-1]:
×
66
                if (
×
67
                    comment["creation_time"] == bug_history["status_time"]
68
                    and comment["author"] == bug_history["status_author"]
69
                ):
70
                    bug_history["resolution_comment"] = comment["text"]
×
71
                    break
×
72
            else:
73
                # Check if the bugbot has already needinfo'ed on the bug since
74
                # the last status change before making one
NEW
75
                skip_ni = False
×
NEW
76
                status_time = lmdutils.get_date_ymd(bug_history["status_time"])
×
NEW
77
                for comment in bug_comments[::-1]:
×
NEW
78
                    if lmdutils.get_date_ymd(comment["creation_time"]) <= status_time:
×
NEW
79
                        break
×
80

NEW
81
                    if comment["author"] == BOT_MAIN_ACCOUNT:
×
NEW
82
                        if (
×
83
                            "comment containing a reason for why"
84
                            "the performance regression"
85
                            in comment["text"]
86
                            or "could you provide a comment explaining the resolution?"
87
                            in comment["text"]
88
                        ):
89
                            # Bugbot has already commented on this bug since the last
90
                            # status change. No need to comment again since this was
91
                            # just a resolution change
NEW
92
                            skip_ni = True
×
93

NEW
94
                if not skip_ni:
×
NEW
95
                    self.extra_ni[bug_id] = bug_history
×
96

97
            self.extra_email_info[bug_id] = bug_history
×
98

NEW
99
    def get_needinfo_nicks(self):
×
NEW
100
        if not self.extra_ni:
×
NEW
101
            return
×
102

NEW
103
        def _user_handler(user, data):
×
NEW
104
            data[user["name"]] = user.get("nick", "")
×
105

NEW
106
        user_emails_to_names = {}
×
NEW
107
        BugzillaUser(
×
108
            user_names=[bug_ni["status_author"] for bug_ni in self.extra_ni.values()],
109
            include_fields=["nick", "name"],
110
            user_handler=_user_handler,
111
            user_data=user_emails_to_names,
112
        ).wait()
113

NEW
114
        for bug_id, bug_info in self.extra_ni.items():
×
NEW
115
            if bug_info["status_author"] in user_emails_to_names:
×
NEW
116
                bug_info["nickname"] = user_emails_to_names[bug_info["status_author"]]
×
117

UNCOV
118
    def comment_handler(self, bug, bug_id, bugs):
×
119
        # Gather all comments to match them with the history after
120
        self._bug_comments[bug_id] = bug["comments"]
×
121

122
    def history_handler(self, bug):
×
123
        bug_info = self._bug_history.setdefault(str(bug["id"]), {})
×
124

125
        # Get the last resolution change that was made in this bug
126
        for change in bug["history"][::-1]:
×
127
            # Get the most recent resolution change first, this is because
128
            # it could have changed since the status was changed and by who
NEW
129
            if not bug_info.get("resolution"):
×
NEW
130
                for specific_change in change["changes"]:
×
NEW
131
                    if specific_change["field_name"] == "resolution":
×
NEW
132
                        bug_info["resolution"] = specific_change["added"]
×
NEW
133
                        bug_info["resolution_previous"] = (
×
134
                            specific_change["removed"].strip() or "---"
135
                        )
NEW
136
                        bug_info["resolution_time"] = change["when"]
×
NEW
137
                        break
×
138

NEW
139
            if bug_info.get("resolution"):
×
140
                # Find the status that the bug was resolved to, and by who
NEW
141
                for specific_change in change["changes"]:
×
NEW
142
                    if specific_change["field_name"] == "status" and specific_change[
×
143
                        "added"
144
                    ] in ("RESOLVED", "REOPENED"):
NEW
145
                        bug_info["status"] = specific_change["added"]
×
NEW
146
                        bug_info["status_author"] = change["who"]
×
NEW
147
                        bug_info["status_time"] = change["when"]
×
NEW
148
                        break
×
149

NEW
150
            if bug_info.get("status"):
×
UNCOV
151
                break
×
152

153
    def gather_bugs_info(self, bugs):
×
154
        Bugzilla(
×
155
            bugids=self.get_list_bugs(bugs),
156
            historyhandler=self.history_handler,
157
            commenthandler=self.comment_handler,
158
            commentdata=bugs,
159
            comment_include_fields=["text", "creation_time", "author"],
160
        ).get_data().wait()
161

162
        # Match the history with comments to get resolution reasons
163
        self.get_resolution_comments(bugs)
×
164

165
        # Get info for needinfos
NEW
166
        self.get_needinfo_nicks()
×
167

NEW
168
    def set_autofix(self, bugs):
×
NEW
169
        for bug_id, bug_info in self.extra_ni.items():
×
NEW
170
            self.add_auto_ni(
×
171
                bug_id,
172
                {
173
                    "mail": bug_info["status_author"],
174
                    "nickname": (
175
                        (":" + bug_info["nickname"])
176
                        if "nickname" in bug_info
177
                        else bug_info["status_author"]
178
                    ),
179
                },
180
            )
181

182
    def get_bugs(self, *args, **kwargs):
×
183
        bugs = super().get_bugs(*args, **kwargs)
×
184
        self.gather_bugs_info(bugs)
×
NEW
185
        self.set_autofix(bugs)
×
UNCOV
186
        return bugs
×
187

188

189
if __name__ == "__main__":
×
NEW
190
    PerfAlertResolvedRegression().run()
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc