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

mozilla / relman-auto-nag / #4605

pending completion
#4605

push

coveralls-python

suhaibmujahid
Some refactoring

Move the fetching of Bugzilla data to `SignaturesDataFetcher`
Move finding actionable crashes to `SignaturesDataFetcher`

646 of 3416 branches covered (18.91%)

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

1828 of 8481 relevant lines covered (21.55%)

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

7
import jinja2
×
8
import requests
×
9

10
from bugbot import logger
×
11
from bugbot.bzcleaner import BzCleaner
×
12
from bugbot.crash import socorro_util
×
13
from bugbot.crash.analyzer import DevBugzilla, SignaturesDataFetcher
×
14

15

16
class FileCrashBug(BzCleaner):
×
17
    """File bugs for new actionable crashes."""
18

19
    # NOTE: If you make changes that affect the output of the rule, you should
20
    # increment this number. This is needed in the experimental phase only.
21
    VERSION = 1
×
22
    MAX_BUG_TITLE_LENGTH = 255
×
23

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

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

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

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

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

44
    def get_bugs(self, date):
×
45
        self.query_url = None
×
46
        bugs = {}
×
47

48
        signatures = SignaturesDataFetcher.find_new_actionable_crashes(
×
49
            "Firefox", "nightly"
50
        )
51

52
        for signature in signatures.analyze():
×
53
            logger.debug("Generating bug for signature: %s", signature.signature_term)
×
54

55
            title = f"Crash in [@ {signature.signature_term}]"
×
56
            if len(title) > self.MAX_BUG_TITLE_LENGTH:
×
57
                title = title[: self.MAX_BUG_TITLE_LENGTH - 3] + "..."
×
58

59
            # TODO: Handle cases where the regressor is a security bug. I such
60
            # cases, we may want to fil the bug as security bug.
61

62
            flags = None
×
63
            if signature.regressed_by:
×
64
                # TODO: check user activity and if the ni? is open
65
                flags = [
×
66
                    {
67
                        "name": "needinfo",
68
                        "requestee": signature.regressed_by_author["name"],
69
                        "status": "?",
70
                        "new": "true",
71
                    }
72
                ]
73

74
            report = signature.fetch_representing_processed_crash()
×
75
            description = self.bug_description_template.render(
×
76
                {
77
                    **socorro_util.generate_bug_description_data(report),
78
                    "signature": signature,
79
                    "needinfo_regression_author": bool(flags),
80
                }
81
            )
82

83
            # TODO: Provide the following information:
84
            # [X] Crash signature
85
            # [X] Top 10 frames of crashing thread
86
            # [X] Component
87
            # [X] The kind of crash
88
            # [ ] Regression window
89
            # [X] Inducing patch
90
            # [X] Reason
91
            # [X] Regressed by
92
            # [X] Platform
93
            # [ ] Firefox status flags
94
            # [ ] Severity
95
            # [ ] Time correlation
96
            # [X] User comments
97
            # [ ] Crash address commonalities
98
            # [ ] Estimated future crash volume
99

100
            bug_data = {
×
101
                "blocks": "bugbot-auto-crash",
102
                "type": "defect",
103
                "keywords": ["crash"],
104
                "status_whiteboard": f"[bugbot-crash-v{self.VERSION}]",
105
                "summary": title,
106
                "product": signature.crash_component.product,
107
                "component": signature.crash_component.name,
108
                "op_sys": signature.bugzilla_op_sys,
109
                "rep_platform": signature.bugzilla_cpu_arch,
110
                "cf_crash_signature": f"[@ {signature.signature_term}]",
111
                "description": description,
112
                # TODO: Uncomment the following lines when we move to file on
113
                # the production instance of Bugzilla. Filling `regressed_by` or
114
                # `flags` on bugzilla-dev will cause "bug does not exist" errors.
115
                # "regressed_by": signature.regressed_by,
116
                # "flags": flags,
117
            }
118

119
            if self.dryrun:
×
120
                logger.info("Dry-run bug:")
×
121
                pprint.pprint(bug_data)
×
122
                bug_id = str(len(bugs) + 1)
×
123
            else:
124
                # NOTE: When moving to production:
125
                #   - Use Bugzilla instead of DevBugzilla
126
                #   - Drop the DevBugzilla class
127
                #   - Update the bug URL `file_crash_bug.html`
128
                #   - Drop the bug link `file_crash_bug_description.md.jinja`
129
                #   - Fill the `regressed_by` and `flags` fields
130
                #   - Create the bug using `utils.create_bug``
131
                resp = requests.post(
×
132
                    url=DevBugzilla.API_URL,
133
                    json=bug_data,
134
                    headers=DevBugzilla([]).get_header(),
135
                    verify=True,
136
                    timeout=DevBugzilla.TIMEOUT,
137
                )
138
                resp.raise_for_status()
×
139
                bug = resp.json()
×
140
                bug_id = str(bug["id"])
×
141
                # TODO: log the created bugs info somewhere (e.g., DB,
142
                # spreadsheet, or LabelStudio)
143

144
            bugs[bug_id] = {
×
145
                "id": bug_id,
146
                "summary": title,
147
                "component": signature.crash_component,
148
            }
149

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

152
        return bugs
×
153

154

155
if __name__ == "__main__":
×
156
    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