Keep the errors as we are seeing them and print or send a report at the end
While the script runs, we're keeping in memory a list of all the errors we have encountered and at the end of the run we send to the admins a report with all of them, categorized in a way that will hopefully make it easier to fix for them. We're also adding an option to preserve the function to send the report to the admins but disable sending the notifications to the users/packagers. This would be useful to start get an idea of the number of people who would have issues with the script. Signed-off-by: Pierre-Yves Chibon <pingou@pingoured.fr>
This commit is contained in:
parent
9b3b02b4ac
commit
9283999992
1 changed files with 72 additions and 21 deletions
|
@ -30,6 +30,7 @@ into bugzilla.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
|
import collections
|
||||||
import datetime
|
import datetime
|
||||||
from email.message import EmailMessage
|
from email.message import EmailMessage
|
||||||
import itertools
|
import itertools
|
||||||
|
@ -126,6 +127,7 @@ class BugzillaProxy:
|
||||||
self.product_cache = {}
|
self.product_cache = {}
|
||||||
self.user_cache = {}
|
self.user_cache = {}
|
||||||
self.inverted_user_cache = {}
|
self.inverted_user_cache = {}
|
||||||
|
self.errors = []
|
||||||
|
|
||||||
# Connect to the fedora account system
|
# Connect to the fedora account system
|
||||||
self.fas = AccountSystem(
|
self.fas = AccountSystem(
|
||||||
|
@ -286,8 +288,13 @@ class BugzillaProxy:
|
||||||
initial_cc_emails.append(bz_email)
|
initial_cc_emails.append(bz_email)
|
||||||
initial_cc_fasnames.append(watcher)
|
initial_cc_fasnames.append(watcher)
|
||||||
else:
|
else:
|
||||||
print(f"** {watcher} has no bugzilla_email or mailing_list set "
|
self.errors.append(
|
||||||
f"({collection}/{package}) **")
|
f"{watcher} has no bugzilla_email or mailing_list set on "
|
||||||
|
f"({collection}/{package})"
|
||||||
|
)
|
||||||
|
if self.config["verbose"]:
|
||||||
|
print(f"** {watcher} has no bugzilla_email or mailing_list set "
|
||||||
|
f"({collection}/{package}) **")
|
||||||
|
|
||||||
# Add owner to the cclist so comaintainers taking over a bug don't
|
# Add owner to the cclist so comaintainers taking over a bug don't
|
||||||
# have to do this manually
|
# have to do this manually
|
||||||
|
@ -499,6 +506,7 @@ class DistgitBugzillaSync:
|
||||||
_namespace_to_product = None
|
_namespace_to_product = None
|
||||||
_product_to_branch_regex = None
|
_product_to_branch_regex = None
|
||||||
_branch_regex_to_product = None
|
_branch_regex_to_product = None
|
||||||
|
errors = collections.defaultdict(list)
|
||||||
|
|
||||||
def send_email(self, from_address, to_address, subject, message, cc_address=None):
|
def send_email(self, from_address, to_address, subject, message, cc_address=None):
|
||||||
'''Send an email if there's an error.
|
'''Send an email if there's an error.
|
||||||
|
@ -615,6 +623,11 @@ class DistgitBugzillaSync:
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--print-no-change', action='store_true', default=False,
|
'--print-no-change', action='store_true', default=False,
|
||||||
help="Print elements that are not being changed as they are checked")
|
help="Print elements that are not being changed as they are checked")
|
||||||
|
parser.add_argument(
|
||||||
|
'--no-user-notifications', dest="user_notifications", action='store_false',
|
||||||
|
default=True,
|
||||||
|
help="Do not notify every packager whose account is wrongly set-up, but do send the "
|
||||||
|
"overall report to the admins")
|
||||||
|
|
||||||
self.args = parser.parse_args()
|
self.args = parser.parse_args()
|
||||||
|
|
||||||
|
@ -723,19 +736,18 @@ class DistgitBugzillaSync:
|
||||||
if project['namespace'] not in self.env['pdc_types']:
|
if project['namespace'] not in self.env['pdc_types']:
|
||||||
project['branches'] = []
|
project['branches'] = []
|
||||||
project['products'] = []
|
project['products'] = []
|
||||||
if self.env["verbose"]:
|
self.errors["configuration"].append(
|
||||||
print(
|
f'Namespace `{project["namespace"]}` not found in the pdc_type '
|
||||||
f'! Namespace {project["namespace"]} not found in the pdc_type '
|
f'configuration key, project {project["namespace"]}/{project["name"]} '
|
||||||
f'configuration key, project {project["namespace"]}/{project["name"]} '
|
'ignored'
|
||||||
'ignored'
|
)
|
||||||
)
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
pdc_type = self.env['pdc_types'][project['namespace']]
|
pdc_type = self.env['pdc_types'][project['namespace']]
|
||||||
project['branches'] = pdc_branches.get(pdc_type, {}).get(project['name'], [])
|
project['branches'] = pdc_branches.get(pdc_type, {}).get(project['name'], [])
|
||||||
if not project['branches']:
|
if not project['branches']:
|
||||||
if self.env["verbose"]:
|
self.errors["PDC"].append(
|
||||||
print(f"! No PDC branch found for {project['namespace']}/{project['name']}")
|
f"No PDC branch found for {project['namespace']}/{project['name']}")
|
||||||
|
|
||||||
# Products
|
# Products
|
||||||
products = set()
|
products = set()
|
||||||
|
@ -782,10 +794,18 @@ class DistgitBugzillaSync:
|
||||||
if self.env["verbose"]:
|
if self.env["verbose"]:
|
||||||
print('Querying {0}'.format(pagure_override_url))
|
print('Querying {0}'.format(pagure_override_url))
|
||||||
override_rv = session.get(pagure_override_url, timeout=30)
|
override_rv = session.get(pagure_override_url, timeout=30)
|
||||||
|
output = {}
|
||||||
if override_rv.status_code == 200:
|
if override_rv.status_code == 200:
|
||||||
override_yaml = yaml.safe_load(override_rv.text)
|
try:
|
||||||
return override_yaml.get('bugzilla_contact', {})
|
override_yaml = yaml.safe_load(override_rv.text)
|
||||||
return {}
|
output = override_yaml.get('bugzilla_contact', {})
|
||||||
|
except yaml.YAMLError:
|
||||||
|
self.errors["SCM overrides"].append(
|
||||||
|
f"Failed to load yaml file at: {pagure_override_url}")
|
||||||
|
except AttributeError:
|
||||||
|
self.errors["SCM overrides"].append(
|
||||||
|
f"Invalid yaml file at: {pagure_override_url}")
|
||||||
|
return output
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def main(cls):
|
def main(cls):
|
||||||
|
@ -895,33 +915,64 @@ class DistgitBugzillaSync:
|
||||||
)
|
)
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
# A username didn't have a bugzilla address
|
# A username didn't have a bugzilla address
|
||||||
errors.append(str(e.args))
|
self.errors["bugzilla_raw"].append(str(e.args))
|
||||||
|
self.errors["bugzilla"].append(
|
||||||
|
f"Failed to update: `{product}/{project['name']}`:"
|
||||||
|
f"\n {e}"
|
||||||
|
f"\n {e.args}"
|
||||||
|
)
|
||||||
except DataChangedError as e:
|
except DataChangedError as e:
|
||||||
# A Package or Collection was returned via xmlrpc but wasn't
|
# A Package or Collection was returned via xmlrpc but wasn't
|
||||||
# present when we tried to change it
|
# present when we tried to change it
|
||||||
errors.append(str(e.args))
|
self.errors["bugzilla_raw"].append(str(e.args))
|
||||||
|
self.errors["bugzilla"].append(
|
||||||
|
f"Failed to update: `{product}/{project['name']}`: "
|
||||||
|
f"\n {e}"
|
||||||
|
f"\n {e.args}"
|
||||||
|
)
|
||||||
except xmlrpc.client.ProtocolError as e:
|
except xmlrpc.client.ProtocolError as e:
|
||||||
# Unrecoverable and likely means that nothing is going to
|
# Unrecoverable and likely means that nothing is going to
|
||||||
# succeed.
|
# succeed.
|
||||||
errors.append(str(e.args))
|
self.errors["bugzilla_raw"].append(str(e.args))
|
||||||
|
self.errors["bugzilla"].append(
|
||||||
|
f"Failed to update: `{product}/{project['name']}`: "
|
||||||
|
f"\n {e}"
|
||||||
|
f"\n {e.args}"
|
||||||
|
)
|
||||||
break
|
break
|
||||||
except xmlrpc.client.Error as e:
|
except xmlrpc.client.Error as e:
|
||||||
# An error occurred in the xmlrpc call. Shouldn't happen but
|
# An error occurred in the xmlrpc call. Shouldn't happen but
|
||||||
# we better see what it is
|
# we better see what it is
|
||||||
errors.append('%s -- %s' % (project["name"], e.args[-1]))
|
self.errors["bugzilla_raw"].append('%s -- %s' % (project["name"], e.args[-1]))
|
||||||
|
self.errors["bugzilla"].append(
|
||||||
|
f"Failed to update: `{product}/{project['name']}`: "
|
||||||
|
f"\n {e}"
|
||||||
|
f"\n {e.args}"
|
||||||
|
)
|
||||||
|
|
||||||
# Send notification of errors
|
# Send notification of errors
|
||||||
if errors:
|
if self.errors:
|
||||||
|
if not self.env["dryrun"] and self.args.user_notifications:
|
||||||
|
self.notify_users(self.errors["bugzilla"])
|
||||||
|
|
||||||
|
# Build the report for the admins
|
||||||
|
report = ["ERROR REPORT"]
|
||||||
|
for key in ["configuration", "PDC", "SCM overrides", "bugzilla"]:
|
||||||
|
if self.errors[key]:
|
||||||
|
report.append(key)
|
||||||
|
report.append(' - {}'.format("\n - ".join(self.errors[key])))
|
||||||
|
report.append('')
|
||||||
|
|
||||||
if self.env["verbose"] or self.env["dryrun"]:
|
if self.env["verbose"] or self.env["dryrun"]:
|
||||||
print('[DEBUG]', '\n'.join(errors))
|
print("*" * 80)
|
||||||
|
print('\n'.join(report))
|
||||||
else:
|
else:
|
||||||
self.notify_users(errors)
|
|
||||||
self.send_email(
|
self.send_email(
|
||||||
self.env['email']['from'],
|
self.env['email']['from'],
|
||||||
self.env['email']['notify_admins'],
|
self.env['email']['notify_admins'],
|
||||||
'Errors while syncing bugzilla with the PackageDB',
|
'Errors while syncing bugzilla with the PackageDB',
|
||||||
self.env['email']['templates']['admin_notification'].format(
|
self.env['email']['templates']['admin_notification'].format(
|
||||||
errors='\n'.join(errors)
|
errors='\n'.join(report)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue