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

mozilla / relman-auto-nag / #4994

09 May 2024 04:40PM CUT coverage: 21.827% (+0.01%) from 21.817%
#4994

push

coveralls-python

benjaminmah
Reverted refactoring changes

716 of 3592 branches covered (19.93%)

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

1928 of 8833 relevant lines covered (21.83%)

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
×
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
×
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
×
22
from bugbot.component_triagers import ComponentName, ComponentTriagers, TriageOwner
×
23

24

25
class TriageOwnerRotations(BzCleaner):
×
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
×
42
        self.has_put_errors = False
×
43

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

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

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:
×
55
            logger.info(
×
56
                "The triage owner for '%s' will be: '%s'",
57
                new_triager.component,
58
                new_triager.bugzilla_email,
59
            )
60
            error_occurred = False
×
61

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

75
            old_owner = self.component_triagers.get_current_triage_owner(
×
76
                new_triager.component
77
            )
78
            details_url = self.convert_to_url(str(new_triager.component))
×
79

80
            self.send_email_to_triage_owners(
×
81
                old_owner,
82
                new_triager.bugzilla_email,
83
                new_triager.component,
84
                details_url,
85
                error_occurred,
86
            )
87

88
        return failures
×
89

90
    @retry(
×
91
        retry=retry_if_exception_message(match=r"^\d{3} Server Error"),
92
        wait=wait_exponential(),
93
        stop=stop_after_attempt(3),
94
    )
95
    def _put_new_triage_owner(self, new_triager: TriageOwner) -> None:
×
96
        change = {"triage_owner": new_triager.bugzilla_email}
×
97
        BugzillaComponent(
×
98
            new_triager.component.product,
99
            new_triager.component.name,
100
        ).put(change)
101

102
    def get_email_data(self, date: str) -> List[dict]:
×
103
        new_triagers = self.component_triagers.get_new_triage_owners()
×
104
        failures = self._update_triage_owners(new_triagers)
×
105
        self.has_put_errors = len(failures) > 0
×
106

NEW
107
        return [
×
108
            {
109
                "component": new_triager.component,
110
                "old_triage_owner": self.component_triagers.get_current_triage_owner(
111
                    new_triager.component
112
                ),
113
                "new_triage_owner": new_triager.bugzilla_email,
114
                "has_put_error": new_triager.component in failures,
115
            }
116
            for new_triager in new_triagers
117
        ]
118

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

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

140
        subject = "Triage Owner Update"
×
141

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

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

155
        encoded = quote_plus(component)
×
156

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

159
        return url
×
160

161

162
if __name__ == "__main__":
×
163
    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