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

mozilla / relman-auto-nag / #4992

09 May 2024 01:47PM CUT coverage: 21.825% (-0.04%) from 21.86%
#4992

push

coveralls-python

benjaminmah
Added url creation for BugDash triage

716 of 3592 branches covered (19.93%)

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

27 existing lines in 1 file now uncovered.

1928 of 8834 relevant lines covered (21.82%)

0.22 hits per line

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

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

6
from typing import List, Set
×
NEW
7
from urllib.parse import quote_plus
×
8

9
from jinja2 import Environment, FileSystemLoader
×
10
from libmozdata.bugzilla import BugzillaComponent
×
11
from requests import HTTPError
×
UNCOV
12
from tenacity import (
×
13
    RetryError,
14
    retry,
15
    retry_if_exception_message,
16
    stop_after_attempt,
17
    wait_exponential,
18
)
19

20
from bugbot import logger, mail
×
21
from bugbot.bzcleaner import BzCleaner
×
UNCOV
22
from bugbot.component_triagers import ComponentName, ComponentTriagers, TriageOwner
×
23

24

25
class TriageOwnerRotations(BzCleaner):
×
UNCOV
26
    def __init__(
×
27
        self,
28
        excluded_teams: List[str] = [
29
            "Layout",
30
            "GFX",
31
        ],
32
    ) -> None:
33
        """Constructor
34

35
        Args:
36
            excluded_teams: teams to excluded all of their components when
37
                performing the triage owner rotation.
38
        """
39
        super().__init__()
×
40
        self.component_triagers = ComponentTriagers(excluded_teams=excluded_teams)
×
41
        self.query_url = None
×
UNCOV
42
        self.has_put_errors = False
×
43

44
    def description(self) -> str:
×
UNCOV
45
        return "Triage owners that got updated"
×
46

47
    def get_extra_for_template(self):
×
UNCOV
48
        return {"has_put_errors": self.has_put_errors}
×
49

UNCOV
50
    def _update_triage_owners(
×
51
        self, new_triage_owners: List[TriageOwner]
52
    ) -> Set[ComponentName]:
53
        failures = set()
×
54
        for new_triager in new_triage_owners:
×
UNCOV
55
            logger.info(
×
56
                "The triage owner for '%s' will be: '%s'",
57
                new_triager.component,
58
                new_triager.bugzilla_email,
59
            )
60

61
            if not self.dryrun and not self.test_mode:
×
62
                try:
×
63
                    self._put_new_triage_owner(new_triager)
×
64
                except (HTTPError, RetryError) as err:
×
65
                    failures.add(new_triager.component)
×
UNCOV
66
                    logger.exception(
×
67
                        "Cannot update the triage owner for '%s' to be '%s': %s",
68
                        new_triager.component,
69
                        new_triager.bugzilla_email,
70
                        err,
71
                    )
72

UNCOV
73
        return failures
×
74

UNCOV
75
    @retry(
×
76
        retry=retry_if_exception_message(match=r"^\d{3} Server Error"),
77
        wait=wait_exponential(),
78
        stop=stop_after_attempt(3),
79
    )
80
    def _put_new_triage_owner(self, new_triager: TriageOwner) -> None:
×
81
        change = {"triage_owner": new_triager.bugzilla_email}
×
UNCOV
82
        BugzillaComponent(
×
83
            new_triager.component.product,
84
            new_triager.component.name,
85
        ).put(change)
86

87
    def get_email_data(self, date: str) -> List[dict]:
×
88
        new_triagers = self.component_triagers.get_new_triage_owners()
×
89
        failures = self._update_triage_owners(new_triagers)
×
UNCOV
90
        self.has_put_errors = len(failures) > 0
×
91

UNCOV
92
        email_data = []
×
93

UNCOV
94
        for new_triager in new_triagers:
×
UNCOV
95
            old_owner = self.component_triagers.get_current_triage_owner(
×
96
                new_triager.component
97
            )
98

UNCOV
99
            email_data.append(
×
100
                {
101
                    "component": new_triager.component,
102
                    "old_triage_owner": old_owner,
103
                    "new_triage_owner": new_triager.bugzilla_email,
104
                    "has_put_error": new_triager.component in failures,
105
                }
106
            )
107

NEW
108
            url = convert_to_url(str(new_triager.component))
×
109

UNCOV
110
            self.send_email_to_triage_owners(
×
111
                old_owner,
112
                new_triager.bugzilla_email,
113
                new_triager.component,
114
                url,
115
            )
116

UNCOV
117
        return email_data
×
118

UNCOV
119
    def send_email_to_triage_owners(self, old_email, new_email, component, details_url):
×
120
        """Send an email to the old and new triage owners about the switch."""
UNCOV
121
        env = Environment(loader=FileSystemLoader("templates"))
×
UNCOV
122
        template = env.get_template("triage_owner_rotations_2.html")
×
123

UNCOV
124
        body = template.render(
×
125
            preamble="Triage Owner Update Notification",
126
            data=[
127
                {
128
                    "component": component,
129
                    "old_triage_owner": old_email,
130
                    "new_triage_owner": new_email,
131
                    "details_url": details_url,
132
                    "has_put_error": False,
133
                }
134
            ],
135
            table_attrs="",
136
        )
137

UNCOV
138
        subject = "Triage Owner Update"
×
139

UNCOV
140
        mail.send(
×
141
            From="xxx@xxxx.xxx",
142
            To=[old_email, new_email],
143
            Subject=subject,
144
            Body=body,
145
            html=True,
146
            dryrun=True,
147
        )
148

149

NEW
150
def convert_to_url(component: str) -> str:
×
151
    # replace double colons with a single colon
NEW
152
    component = component.replace("::", ":")
×
153

NEW
154
    encoded = quote_plus(component)
×
155

NEW
156
    url = "https://bugdash.moz.tools/?component=" + encoded
×
157

NEW
158
    return url
×
159

160

UNCOV
161
if __name__ == "__main__":
×
UNCOV
162
    TriageOwnerRotations().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