1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
|
# Copyright 2024 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Code for interacting with Buganizer."""
import datetime
from typing import Iterable, Set
from blinkpy.w3c import buganizer
from bad_machine_finder import detection
_AUTOMATED_COMMENT_START = 'Automated Report Of Bad Machines'
class BuganizerException(Exception):
"""A general exception for Buganizer-related errors."""
class ClientNotAvailableException(BuganizerException):
"""Indicates that a Buganzier client could not be created."""
class BugNotAccessibleException(BuganizerException):
"""Indicates that the specified bug could not be accessed."""
def UpdateBug(bug_id: int,
mixin_grouped_bad_machines: detection.MixinGroupedBadMachines,
grace_period: int) -> None:
"""Updates the given |bug_id| with bad machine results.
Will automatically omit bad machines that have previously been reported in the
past |grace_period| days.
Args:
bug_id: The Buganizer bug ID to update.
mixin_groupd_bad_machines: A MixinGroupedBadMachines object containing all
of the bad machine data to use when updating the bug.
grace_period: The minimum number of days between when a bad machine was
reported and when it can be reported again.
"""
client = _GetBuganizerClient()
bad_machine_names = mixin_grouped_bad_machines.GetAllBadMachineNames()
recently_reported_bots = _GetRecentlyReportedBots(bug_id, client,
bad_machine_names,
grace_period)
markdown_components = [
_AUTOMATED_COMMENT_START,
]
mixin_report_markdown = mixin_grouped_bad_machines.GenerateMarkdown(
bots_to_skip=recently_reported_bots)
if mixin_report_markdown:
markdown_components.append(mixin_report_markdown)
else:
markdown_components.append('No new bad machines detected')
markdown_comment = '\n\n'.join(markdown_components)
client.NewComment(bug_id, markdown_comment, use_markdown=True)
def _GetRecentlyReportedBots(bug_id: int, client: buganizer.BuganizerClient,
bad_machine_names: Iterable[str],
grace_period: int) -> Set[str]:
"""Retrieves the subset of |bad_machine_names| which were reported recently.
Args:
bug_id: The Buganizer bug ID to check.
client: The BuganizerClient to use for interacting with Buganizer.
bad_machine_names: All machine names that should be looked for within
recent comments on the bug.
grace_period: The minimum number of days since the last mention of a machine
on the bug for it not to be considered recently reported.
Returns:
A set of machine names which were reported on |bug_id| within the past
|grace_period| days. Guaranteed to be a subset of |bad_machine_names|.
"""
comment_list = client.GetIssueComments(bug_id)
# GetIssueComments currently returns a dict if something goes wrong instead of
# raising an exception.
# TODO(crbug.com/361602059): Switch to catching exceptions once those are
# raised.
if isinstance(comment_list, dict):
raise BugNotAccessibleException(
f'Failed to get comments from {bug_id}: '
f'{comment_list.get("error", "error not provided")}')
recent_comment_bodies = []
for c in comment_list:
comment_iso_timestamp = c['timestamp']
# Z indicates the UTC timezone, but is not supported until Python 3.11.
if comment_iso_timestamp.endswith(('z', 'Z')):
comment_iso_timestamp = comment_iso_timestamp[:-1]
comment_date = datetime.datetime.fromisoformat(comment_iso_timestamp)
n_days_ago = (datetime.datetime.now(comment_date.tzinfo) -
datetime.timedelta(days=grace_period))
if comment_date < n_days_ago:
continue
if _AUTOMATED_COMMENT_START not in c['comment']:
continue
recent_comment_bodies.append(c['comment'])
recently_reported_bots = set()
for bot_id in bad_machine_names:
for cb in recent_comment_bodies:
if bot_id in cb:
recently_reported_bots.add(bot_id)
break
return recently_reported_bots
def _GetBuganizerClient() -> buganizer.BuganizerClient:
try:
return buganizer.BuganizerClient()
except Exception as e: # pylint: disable=broad-except
raise ClientNotAvailableException(
'Failed to create Buganizer client') from e
|