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

mozilla / relman-auto-nag / #4800

30 Oct 2023 03:36PM CUT coverage: 22.058% (-0.03%) from 22.083%
#4800

push

coveralls-python

web-flow
Merge branch 'master' into crash-extra-filter

716 of 3574 branches covered (0.0%)

34 of 34 new or added lines in 2 files covered. (100.0%)

1925 of 8727 relevant lines covered (22.06%)

0.22 hits per line

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

0.0
/bugbot/rules/file_crash_bug.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

5
import pprint
×
6
from functools import cached_property
×
7

8
import humanize
×
9
import jinja2
×
10
import requests
×
11

12
from bugbot import logger, utils
×
13
from bugbot.bug.analyzer import BugAnalyzer
×
14
from bugbot.bzcleaner import BzCleaner
×
15
from bugbot.crash import socorro_util
×
16
from bugbot.crash.analyzer import SignatureAnalyzer, SignaturesDataFetcher
×
17
from bugbot.user_activity import UserActivity, UserStatus
×
18

19

20
class FileCrashBug(BzCleaner):
×
21
    """File bugs for new actionable crashes."""
22

23
    MAX_BUG_TITLE_LENGTH = 255
×
24

25
    def __init__(self):
×
26
        super().__init__()
×
27

28
        self.bug_description_template = jinja2.Environment(
×
29
            loader=jinja2.FileSystemLoader("templates")
30
        ).get_template("file_crash_bug_description.md.jinja")
31

32
    def description(self):
×
33
        return "New actionable crashes"
×
34

35
    def columns(self):
×
36
        return ["component", "id", "summary"]
×
37

38
    def get_bz_params(self, date):
×
39
        return {
×
40
            "resolution": ["---", "FIXED"],
41
            "keywords": ["feature", "regression"],
42
            "keywords_type": "allwords",
43
        }
44

45
    @cached_property
×
46
    def nightly_version(self) -> int:
×
47
        """The version for the nightly channel as defined on Bugzilla."""
48
        return utils.get_nightly_version_from_bz()
×
49

50
    def _active_regression_authors(
×
51
        self, signatures: list[SignatureAnalyzer]
52
    ) -> set[str]:
53
        """Get Bugzilla usernames for users who are active and can be needinfo'd.
54

55
        Args:
56
            signatures: a list of signatures for which to check the status of
57
                their regression author.
58

59
        Returns:
60
            A set of user emails.
61
        """
62
        ni_skiplist = self.get_auto_ni_skiplist()
×
63
        users = UserActivity(include_fields=["requests"]).check_users(
×
64
            (
65
                signature.regressed_by_author["name"]
66
                for signature in signatures
67
                if signature.regressed_by_author
68
            ),
69
            keep_active=True,
70
            fetch_employee_info=True,
71
        )
72

73
        return {
×
74
            name
75
            for name, user in users.items()
76
            if name not in ni_skiplist
77
            and user["status"] == UserStatus.ACTIVE
78
            and not user["requests"]["needinfo"]["blocked"]
79
        }
80

81
    def get_bugs(self, date):
×
82
        self.query_url = None
×
83
        bugs = {}
×
84

85
        data_fetcher = SignaturesDataFetcher.find_new_actionable_crashes(
×
86
            "Firefox", "nightly"
87
        )
88
        signatures = data_fetcher.analyze()
×
89
        # This is the last filtering stage which aims to avoid filing bugs for
90
        # junky crashes, where the volume is low and the crashes do not show
91
        # signals of being actionable or critical.
92
        signatures = [
×
93
            signature
94
            for signature in signatures
95
            if signature.num_installs > 5
96
            or signature.num_crashes > 25
97
            or signature.is_potential_near_null_crash
98
            or signature.is_potential_security_crash
99
            or signature.has_moz_crash_reason("MOZ_RELEASE_ASSERT")
100
            or signature.has_moz_crash_reason("DocumentChannel::SetLoadFlags")
101
        ]
102

103
        signature_details_delta = humanize.naturaldelta(data_fetcher.SUMMARY_DURATION)
×
104

105
        active_regression_authors = self._active_regression_authors(signatures)
×
106

107
        for signature in signatures:
×
108
            logger.debug("Generating bug for signature: %s", signature.signature_term)
×
109

110
            title = (
×
111
                f"Startup crash in [@ {signature.signature_term}]"
112
                if signature.is_startup_related_crash
113
                else f"Crash in [@ {signature.signature_term}]"
114
            )
115
            if len(title) > self.MAX_BUG_TITLE_LENGTH:
×
116
                title = title[: self.MAX_BUG_TITLE_LENGTH - 3] + "..."
×
117

118
            # Whether we should needinfo the regression author.
119
            needinfo_regression_author = (
×
120
                signature.regressed_by
121
                and signature.regressed_by_author["email"] in active_regression_authors
122
            )
123

124
            report = signature.fetch_representative_processed_crash()
×
125
            description = self.bug_description_template.render(
×
126
                {
127
                    **socorro_util.generate_bug_description_data(report),
128
                    "signature": signature,
129
                    "needinfo_regression_author": needinfo_regression_author,
130
                    "signature_details_delta": signature_details_delta,
131
                    "signature_details_channel": "Nightly",
132
                }
133
            )
134

135
            # TODO: Provide the following information:
136
            # [X] Crash signature
137
            # [X] Top 10 frames of crashing thread
138
            # [X] Component
139
            # [X] The kind of crash
140
            # [ ] Regression window
141
            # [X] Inducing patch
142
            # [X] Reason
143
            # [X] Regressed by
144
            # [X] Platform
145
            # [ ] Firefox status flags
146
            # [ ] Severity
147
            # [ ] Time correlation
148
            # [X] User comments
149
            # [X] Crash address commonalities
150
            # [ ] Estimated future crash volume
151

152
            bug_data = {
×
153
                "blocks": "bugbot-auto-crash",
154
                "type": "defect",
155
                "keywords": ["crash"],
156
                "summary": title,
157
                "product": signature.crash_component.product,
158
                "component": signature.crash_component.name,
159
                "op_sys": signature.bugzilla_op_sys,
160
                "rep_platform": signature.bugzilla_cpu_arch,
161
                "cf_crash_signature": f"[@ {signature.signature_term}]",
162
                "description": description,
163
                # NOTE(suhaib): the following CC is for testing purposes only
164
                # to allow us access and evaluate security bugs. It should be
165
                # removed at some point after we move to production.
166
                "cc": [
167
                    "smujahid@mozilla.com",
168
                    "mcastelluccio@mozilla.com",
169
                    "aryx.bugmail@gmx-topmail.de",
170
                ],
171
            }
172

173
            if needinfo_regression_author:
×
174
                bug_data["flags"] = [
×
175
                    {
176
                        "name": "needinfo",
177
                        "requestee": signature.regressed_by_author["name"],
178
                        "status": "?",
179
                        "new": "true",
180
                    }
181
                ]
182

183
            if signature.regressed_by:
×
184
                bug_data["keywords"].append("regression")
×
185

186
            if signature.regressed_by:
×
187
                bug_data["regressed_by"] = [signature.regressed_by]
×
188

189
            if signature.is_potential_security_crash:
×
190
                bug_data["groups"] = ["core-security"]
×
191

192
            if "regressed_by" in bug_data:
×
193
                bug_analyzer = BugAnalyzer(bug_data, signature.bugs_store)
×
194
                updates = bug_analyzer.detect_version_status_updates()
×
195
                for update in updates:
×
196
                    bug_data[update.flag] = update.status
×
197

198
                if bug_data["regressed_by"] and not updates:
×
199
                    # If we don't set the nightly flag here, the bot will set it
200
                    # later as part of `regression_new_set_nightly_affected` rule.
201
                    nightly_flag = utils.get_flag(
×
202
                        self.nightly_version, "status", "nightly"
203
                    )
204
                    bug_data[nightly_flag] = "affected"
×
205

206
            if self.dryrun:
×
207
                logger.info("Dry-run bug:")
×
208
                pprint.pprint(bug_data)
×
209
                bug_id = str(len(bugs) + 1)
×
210
            else:
211
                try:
×
212
                    bug = utils.create_bug(bug_data)
×
213
                except requests.HTTPError as err:
×
214
                    logger.exception(
×
215
                        "Failed to create a bug for signature %s: %s",
216
                        signature.signature_term,
217
                        err.response.text,
218
                    )
219
                    continue
×
220

221
                bug_id = str(bug["id"])
×
222
                # TODO: log the created bugs info somewhere (e.g., DB,
223
                # spreadsheet, or LabelStudio)
224

225
            bugs[bug_id] = {
×
226
                "id": bug_id,
227
                "summary": "..." if signature.is_potential_security_crash else title,
228
                "component": signature.crash_component,
229
            }
230

231
        logger.debug("Total of %d bugs have been filed", len(bugs))
×
232

233
        return bugs
×
234

235

236
if __name__ == "__main__":
×
237
    FileCrashBug().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