зеркало из https://github.com/mozilla/bugbot.git
bugzilla scripts folders deleted
This commit is contained in:
Родитель
542f060ff7
Коммит
d13348e0bd
|
@ -1,16 +0,0 @@
|
|||
__all__ = ['models', 'utils', 'agents']
|
||||
|
||||
import httplib
|
||||
from remoteobjects import http
|
||||
|
||||
# Printing throws an error if we are printing using ascii
|
||||
import sys
|
||||
reload(sys)
|
||||
sys.setdefaultencoding('utf-8')
|
||||
|
||||
# Monkey patch remoteobjects to accept 202 status codes.
|
||||
http.HttpObject.response_has_content[httplib.ACCEPTED] = False
|
||||
|
||||
|
||||
VERSION = (0, 0, 1)
|
||||
__version__ = '.'.join(map(str, VERSION))
|
|
@ -1,43 +0,0 @@
|
|||
import os
|
||||
from bugzilla.models import BugSearch, Bug
|
||||
from bugzilla.utils import urljoin, qs
|
||||
|
||||
|
||||
class InvalidAPI_ROOT(Exception):
|
||||
def __str__(self):
|
||||
return "Invalid API url specified. " + \
|
||||
"Please set BZ_API_ROOT in your environment " + \
|
||||
"or pass it to the agent constructor"
|
||||
|
||||
|
||||
class BugzillaAgent(object):
|
||||
def __init__(self, api_root=None, api_key=None):
|
||||
|
||||
if not api_root:
|
||||
api_root = os.environ.get('BZ_API_ROOT')
|
||||
if not api_root:
|
||||
raise InvalidAPI_ROOT
|
||||
self.API_ROOT = api_root
|
||||
|
||||
self.api_key = api_key
|
||||
|
||||
def get_bug(self, bug, include_fields='_default,token,cc,keywords,whiteboard,comments', exclude_fields=None, params={}):
|
||||
params['include_fields'] = [include_fields]
|
||||
params['exclude_fields'] = [exclude_fields]
|
||||
|
||||
url = urljoin(self.API_ROOT, 'bug/%s?%s' % (bug, self.qs(**params)))
|
||||
return Bug.get(url)
|
||||
|
||||
def get_bug_list(self, params={}):
|
||||
url = urljoin(self.API_ROOT, 'bug/?%s' % (self.qs(**params)))
|
||||
return BugSearch.get(url).bugs
|
||||
|
||||
def qs(self, **params):
|
||||
if self.api_key:
|
||||
params['api_key'] = [self.api_key]
|
||||
return qs(**params)
|
||||
|
||||
|
||||
class BMOAgent(BugzillaAgent):
|
||||
def __init__(self, api_key=None):
|
||||
super(BMOAgent, self).__init__('https://bugzilla.mozilla.org/bzapi/', api_key)
|
|
@ -1,23 +0,0 @@
|
|||
from datetime import datetime
|
||||
|
||||
from remoteobjects import fields
|
||||
import dateutil.parser
|
||||
|
||||
|
||||
class StringBoolean(fields.Field):
|
||||
"""Decodes a boolean hidden in a string."""
|
||||
|
||||
def decode(self, value):
|
||||
return bool(int(value))
|
||||
|
||||
|
||||
class Datetime(fields.Datetime):
|
||||
"""Uses python-dateutil for working with datetimes."""
|
||||
|
||||
def decode(self, value):
|
||||
return dateutil.parser.parse(value)
|
||||
|
||||
def encode(self, value):
|
||||
if not isinstance(value, datetime):
|
||||
raise TypeError('Value to encode %r is not a datetime' % (value,))
|
||||
return value.replace(microsecond=0).strftime(self.dateformat)
|
|
@ -1,235 +0,0 @@
|
|||
from remoteobjects import RemoteObject as RemoteObject_, fields
|
||||
|
||||
from .fields import StringBoolean, Datetime
|
||||
|
||||
|
||||
# The datetime format is inconsistent.
|
||||
DATETIME_FORMAT_WITH_SECONDS = '%Y-%m-%d %H:%M:%S %z'
|
||||
DATETIME_FORMAT = '%Y-%m-%d %H:%M %Z'
|
||||
|
||||
|
||||
class RemoteObject(RemoteObject_):
|
||||
|
||||
def post_to(self, url):
|
||||
self._location = url
|
||||
self.post(self)
|
||||
return self.api_data['ref']
|
||||
|
||||
def put_to(self, url):
|
||||
self._location = url
|
||||
self.put()
|
||||
|
||||
def _get_location(self):
|
||||
if self.__location is not None:
|
||||
return self.__location
|
||||
else:
|
||||
return self.api_data.get('ref', None)
|
||||
|
||||
def _set_location(self, url):
|
||||
self.__location = url
|
||||
|
||||
_location = property(_get_location, _set_location)
|
||||
|
||||
|
||||
class Bug(RemoteObject):
|
||||
|
||||
id = fields.Field()
|
||||
summary = fields.Field()
|
||||
assigned_to = fields.Object('User')
|
||||
creator = fields.Object('User')
|
||||
target_milestone = fields.Field()
|
||||
attachments = fields.List(fields.Object('Attachment'))
|
||||
comments = fields.List(fields.Object('Comment'))
|
||||
history = fields.List(fields.Object('Changeset'))
|
||||
keywords = fields.List(fields.Object('Keyword'))
|
||||
status = fields.Field()
|
||||
resolution = fields.Field()
|
||||
|
||||
# TODO: These are Mozilla specific and should be generalized
|
||||
cf_blocking_20 = fields.Field()
|
||||
cf_blocking_fennec = fields.Field()
|
||||
cf_crash_signature = fields.Field()
|
||||
|
||||
creation_time = Datetime(DATETIME_FORMAT_WITH_SECONDS)
|
||||
flags = fields.List(fields.Object('Flag'))
|
||||
blocks = fields.List(fields.Field())
|
||||
depends_on = fields.List(fields.Field())
|
||||
url = fields.Field()
|
||||
cc = fields.List(fields.Object('User'))
|
||||
keywords = fields.List(fields.Field())
|
||||
whiteboard = fields.Field()
|
||||
|
||||
op_sys = fields.Field()
|
||||
platform = fields.Field()
|
||||
priority = fields.Field()
|
||||
product = fields.Field()
|
||||
qa_contact = fields.Object('User')
|
||||
severity = fields.Field()
|
||||
see_also = fields.List(fields.Field())
|
||||
version = fields.Field()
|
||||
|
||||
alias = fields.Field()
|
||||
classification = fields.Field()
|
||||
component = fields.Field()
|
||||
is_cc_accessible = StringBoolean()
|
||||
is_everconfirmed = StringBoolean()
|
||||
is_creator_accessible = StringBoolean()
|
||||
last_change_time = Datetime(DATETIME_FORMAT_WITH_SECONDS)
|
||||
ref = fields.Field()
|
||||
|
||||
# Needed for submitting changes.
|
||||
token = fields.Field(api_name='update_token')
|
||||
|
||||
# Time tracking.
|
||||
actual_time = fields.Field()
|
||||
deadline = Datetime(DATETIME_FORMAT_WITH_SECONDS)
|
||||
estimated_time = fields.Field()
|
||||
# groups = fields.Field() # unimplemented
|
||||
percentage_complete = fields.Field()
|
||||
remaining_time = fields.Field()
|
||||
work_time = fields.Field()
|
||||
|
||||
def __repr__(self):
|
||||
return '<Bug %s: "%s">' % (self.id, self.summary)
|
||||
|
||||
def __str__(self):
|
||||
return "[Bug %s] - %s" % (self.id, self.summary)
|
||||
|
||||
def __hash__(self):
|
||||
return self.id
|
||||
|
||||
|
||||
class User(RemoteObject):
|
||||
|
||||
name = fields.Field()
|
||||
real_name = fields.Field()
|
||||
ref = fields.Field()
|
||||
|
||||
def __repr__(self):
|
||||
return '<User "%s">' % self.real_name
|
||||
|
||||
def __str__(self):
|
||||
return self.real_name or self.name
|
||||
|
||||
def __hash__(self):
|
||||
if not self or not self.name:
|
||||
return 0
|
||||
return self.name.__hash__()
|
||||
|
||||
|
||||
class Attachment(RemoteObject):
|
||||
|
||||
# Attachment data.
|
||||
id = fields.Field()
|
||||
attacher = fields.Object('User')
|
||||
creation_time = Datetime(DATETIME_FORMAT_WITH_SECONDS)
|
||||
last_change_time = Datetime(DATETIME_FORMAT_WITH_SECONDS)
|
||||
description = fields.Field()
|
||||
bug_id = fields.Field()
|
||||
bug_ref = fields.Field()
|
||||
|
||||
# File data.
|
||||
file_name = fields.Field()
|
||||
size = fields.Field()
|
||||
content_type = fields.Field()
|
||||
|
||||
# Attachment metadata.
|
||||
flags = fields.List(fields.Object('Flag'))
|
||||
is_obsolete = StringBoolean()
|
||||
is_private = StringBoolean()
|
||||
is_patch = StringBoolean()
|
||||
|
||||
# Used for submitting changes.
|
||||
token = fields.Field()
|
||||
ref = fields.Field()
|
||||
|
||||
# Only with attachmentdata=1
|
||||
data = fields.Field()
|
||||
encoding = fields.Field()
|
||||
|
||||
def __repr__(self):
|
||||
return '<Attachment %s: "%s">' % (self.id, self.description)
|
||||
|
||||
def __hash__(self):
|
||||
return self.id
|
||||
|
||||
|
||||
class Comment(RemoteObject):
|
||||
|
||||
id = fields.Field()
|
||||
creator = fields.Object('User')
|
||||
creation_time = Datetime(DATETIME_FORMAT_WITH_SECONDS)
|
||||
text = fields.Field()
|
||||
is_private = StringBoolean()
|
||||
|
||||
def __repr__(self):
|
||||
return '<Comment by %s on %s>' % (
|
||||
self.creator, self.creation_time.strftime(DATETIME_FORMAT))
|
||||
|
||||
def __str__(self):
|
||||
return self.text
|
||||
|
||||
def __hash__(self):
|
||||
return self.id
|
||||
|
||||
|
||||
class Change(RemoteObject):
|
||||
|
||||
field_name = fields.Field()
|
||||
added = fields.Field()
|
||||
removed = fields.Field()
|
||||
|
||||
def __repr__(self):
|
||||
return '<Change "%s": "%s" -> "%s">' % (self.field_name, self.removed,
|
||||
self.added)
|
||||
|
||||
|
||||
class Changeset(RemoteObject):
|
||||
|
||||
changer = fields.Object('User')
|
||||
changes = fields.List(fields.Object('Change'))
|
||||
change_time = Datetime(DATETIME_FORMAT_WITH_SECONDS)
|
||||
|
||||
def __repr__(self):
|
||||
return '<Changeset by %s on %s>' % (
|
||||
self.changer, self.change_time.strftime(DATETIME_FORMAT))
|
||||
|
||||
|
||||
class Flag(RemoteObject):
|
||||
|
||||
id = fields.Field()
|
||||
name = fields.Field()
|
||||
setter = fields.Object('User')
|
||||
status = fields.Field()
|
||||
requestee = fields.Object('User')
|
||||
type_id = fields.Field()
|
||||
|
||||
def __repr__(self):
|
||||
return '<Flag "%s">' % self.name
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def __hash__(self):
|
||||
return self.id
|
||||
|
||||
|
||||
class Keyword(RemoteObject):
|
||||
|
||||
name = fields.Field()
|
||||
|
||||
def __repr__(self):
|
||||
return '<Keyword "%s">' % self.name
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def __hash__(self):
|
||||
if not self or not self.name:
|
||||
return 0
|
||||
return self.name.__hash__()
|
||||
|
||||
|
||||
class BugSearch(RemoteObject):
|
||||
|
||||
bugs = fields.List(fields.Object('Bug'))
|
|
@ -1,90 +0,0 @@
|
|||
import base64
|
||||
from ConfigParser import ConfigParser
|
||||
import getpass
|
||||
import os
|
||||
import posixpath
|
||||
import urllib
|
||||
|
||||
|
||||
def urljoin(base, *args):
|
||||
"""Remove any leading slashes so no subpaths look absolute."""
|
||||
return posixpath.join(base, *[str(s).lstrip('/') for s in args])
|
||||
|
||||
|
||||
def qs(**kwargs):
|
||||
"""Build a URL query string."""
|
||||
url = ''
|
||||
for k, v in kwargs.iteritems():
|
||||
if k == 'username' or k == 'password':
|
||||
pass
|
||||
for value in v:
|
||||
url += '&%s=%s' % (urllib.quote(k), value)
|
||||
return url
|
||||
|
||||
|
||||
def get_credentials(username=None):
|
||||
|
||||
# Try to get it from the environment first
|
||||
if not username:
|
||||
username = os.environ.get('BZ_USERNAME', None)
|
||||
password = os.environ.get('BZ_PASSWORD', None)
|
||||
|
||||
# Try to get it from the system keychain next
|
||||
if not username and not password:
|
||||
try:
|
||||
import keyring
|
||||
if not username:
|
||||
# Grab the default username as we weren't passed in a specific one
|
||||
username = keyring.get_password("bugzilla", 'default_username')
|
||||
if username:
|
||||
# Get the password for the username
|
||||
password = keyring.get_password("bugzilla", username)
|
||||
except ImportError:
|
||||
# If they don't have the keyring lib, fall back to next method
|
||||
pass
|
||||
|
||||
# Then try a config file in their home directory
|
||||
if not (username and password):
|
||||
rcfile = os.path.expanduser('~/.bztoolsrc')
|
||||
config = ConfigParser()
|
||||
config.add_section('bugzilla')
|
||||
if os.path.exists(rcfile):
|
||||
try:
|
||||
config.read(rcfile)
|
||||
username = config.get('bugzilla', 'username')
|
||||
_password = config.get('bugzilla', 'password')
|
||||
if _password:
|
||||
password = base64.b64decode(_password)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Finally, prompt the user for the info if we didn't get it above
|
||||
if not (username and password):
|
||||
username = raw_input('Bugzilla username: ')
|
||||
password = getpass.getpass('Bugzilla password: ')
|
||||
try:
|
||||
# Save the data to the keyring if possible
|
||||
import keyring
|
||||
keyring.set_password("bugzilla", 'default_username', username)
|
||||
keyring.set_password("bugzilla", username, password)
|
||||
except ImportError:
|
||||
# Otherwise save it to a config file
|
||||
config.set('bugzilla', 'username', username)
|
||||
config.set('bugzilla', 'password', base64.b64encode(password))
|
||||
with open(rcfile, 'wb') as configfile:
|
||||
config.write(configfile)
|
||||
|
||||
return username, password
|
||||
|
||||
|
||||
FILE_TYPES = {
|
||||
'text': 'text/plain',
|
||||
'html': 'text/html',
|
||||
'xml': 'application/xml',
|
||||
'gif': 'image/gif',
|
||||
'jpg': 'image/jpeg',
|
||||
'png': 'image/png',
|
||||
'svg': 'image/svg+xml',
|
||||
'binary': 'application/octet-stream',
|
||||
'xul': 'application/vnd.mozilla.xul+xml',
|
||||
}
|
|
@ -1,145 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
import base64
|
||||
import itertools
|
||||
import os
|
||||
import argparse
|
||||
|
||||
from bugzilla.models import Attachment, Flag, User, Comment
|
||||
from bugzilla.agents import BugzillaAgent
|
||||
from bugzilla.utils import urljoin, get_credentials, FILE_TYPES
|
||||
|
||||
REVIEW = 4
|
||||
|
||||
|
||||
class AttachmentAgent(BugzillaAgent):
|
||||
"""Stores credentials, navigates the site."""
|
||||
|
||||
def attach(self, bug_id, filename, description, patch=False,
|
||||
reviewer=None, comment='', content_type='text/plain'):
|
||||
"""Create an attachment, add a comment, obsolete other attachments."""
|
||||
|
||||
print 'Adding "%s" to %s' % (filename, bug_id)
|
||||
self._attach(bug_id, filename, description, patch,
|
||||
reviewer, content_type)
|
||||
|
||||
bug = self.get_bug(bug_id)
|
||||
|
||||
if comment:
|
||||
print 'Adding the comment'
|
||||
self._comment(bug_id, comment)
|
||||
|
||||
print 'Finding attachments to make obsolete...'
|
||||
self.obsolete(bug)
|
||||
|
||||
def _attach(self, bug_id, filename, description, is_patch=False,
|
||||
reviewer=None, content_type='text/plain'):
|
||||
"""Create a new attachment."""
|
||||
fields = {
|
||||
'data': base64.b64encode(open(filename).read()),
|
||||
'encoding': 'base64',
|
||||
'file_name': filename,
|
||||
'content_type': content_type,
|
||||
'description': description,
|
||||
'is_patch': is_patch,
|
||||
}
|
||||
|
||||
if reviewer is not None:
|
||||
fields['flags'] = [Flag(type_id=REVIEW, status='?',
|
||||
requestee=User(name=reviewer))]
|
||||
|
||||
url = urljoin(self.API_ROOT, 'bug/%s/attachment?%s' % (bug_id, self.qs()))
|
||||
return Attachment(**fields).post_to(url)
|
||||
|
||||
def _comment(self, bug_id, comment):
|
||||
"""Create a new comment."""
|
||||
url = urljoin(self.API_ROOT, 'bug/%s/comment?%s' % (bug_id, self.qs()))
|
||||
return Comment(text=comment).post_to(url)
|
||||
|
||||
def obsolete(self, bug):
|
||||
"""Ask what attachments should be obsoleted."""
|
||||
attachments = [a for a in bug.attachments
|
||||
if not bool(int(a.is_obsolete))]
|
||||
|
||||
if not attachments:
|
||||
return
|
||||
|
||||
print "What attachments do you want to obsolete?"
|
||||
msg = '[{index}] {a.id}: "{a.description}" ({a.file_name})'
|
||||
for index, a in enumerate(attachments):
|
||||
print msg.format(index=index, a=a)
|
||||
|
||||
numbers = raw_input('Enter the numbers (space-separated) of '
|
||||
'attachments to make obsolete:\n').split()
|
||||
|
||||
if not numbers:
|
||||
return
|
||||
|
||||
map_ = dict((str(index), a) for index, a in enumerate(attachments))
|
||||
for num, _ in itertools.groupby(sorted(numbers)):
|
||||
try:
|
||||
self._obsolete(map_[num])
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
def _obsolete(self, attachment):
|
||||
"""Mark an attachment obsolete."""
|
||||
print "Obsoleting", attachment
|
||||
attachment.is_obsolete = True
|
||||
attachment._location += '?%s' % self.qs()
|
||||
attachment.put()
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
# Script options
|
||||
parser = argparse.ArgumentParser(description='Submit Bugzilla attachments')
|
||||
|
||||
parser.add_argument('bug_id',
|
||||
type=int,
|
||||
metavar='BUG',
|
||||
help='Bug number')
|
||||
|
||||
parser.add_argument('filename',
|
||||
metavar='FILE',
|
||||
help='File to upload')
|
||||
|
||||
parser.add_argument('--description',
|
||||
help='Attachment description',
|
||||
required=True)
|
||||
|
||||
parser.add_argument('--patch',
|
||||
action='store_true',
|
||||
help='Is this a patch?')
|
||||
|
||||
parser.add_argument('--reviewer',
|
||||
help='Bugzilla name of someone to r?')
|
||||
|
||||
parser.add_argument('--comment',
|
||||
help='Comment for the attachment')
|
||||
|
||||
parser.add_argument('--content_type',
|
||||
choices=FILE_TYPES,
|
||||
help="File's content_type")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.content_type:
|
||||
args.content_type = FILE_TYPES[args.content_type]
|
||||
|
||||
# Get the API root, default to bugzilla.mozilla.org
|
||||
API_ROOT = os.environ.get('BZ_API_ROOT',
|
||||
'https://api-dev.bugzilla.mozilla.org/latest/')
|
||||
|
||||
# Authenticate
|
||||
username, password = get_credentials()
|
||||
|
||||
# Load the agent
|
||||
bz = AttachmentAgent(API_ROOT, username, password)
|
||||
|
||||
# Attach the file
|
||||
bz.attach(**dict(args._get_kwargs()))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -1,92 +0,0 @@
|
|||
# Post-Mortem and Sign-off automated reminder
|
||||
# Daily cronjob, 6am PT
|
||||
|
||||
import urllib2
|
||||
import datetime
|
||||
import re
|
||||
import os
|
||||
import json
|
||||
import smtplib
|
||||
|
||||
REPLY_TO_EMAIL = 'release-mgmt@mozilla.com'
|
||||
SMTP = 'smtp.mozilla.org'
|
||||
CONFIG_JSON = os.getcwd() + "/bztools/scripts/configs/config.json"
|
||||
config = json.load(open(CONFIG_JSON, 'r'))
|
||||
scripts_dir = os.getcwd() + "/scripts/"
|
||||
|
||||
subject = None
|
||||
toaddrs = ['dev-planning@lists.mozilla.org', 'release-drivers@mozilla.com']
|
||||
|
||||
|
||||
def sendMail(toaddr, options):
|
||||
message = (
|
||||
"From: %s\r\n" % options['username']
|
||||
+ "To: %s\r\n" % toaddr
|
||||
+ "CC: %s\r\n" % options['cclist']
|
||||
+ "Reply-To: %s\r\n" % REPLY_TO_EMAIL
|
||||
+ "Subject: %s\r\n" % options['subject']
|
||||
+ "\r\n"
|
||||
+ options['body'])
|
||||
|
||||
server = smtplib.SMTP_SSL(SMTP, 465)
|
||||
server.set_debuglevel(1)
|
||||
server.login(options['username'], options['password'])
|
||||
# note: toaddrs is required for transport agents, the msg['To'] header is not modified
|
||||
server.sendmail(options['username'], toaddr, message)
|
||||
server.quit()
|
||||
|
||||
|
||||
def getTemplateValue(url):
|
||||
version_regex = re.compile(".*<p>(.*)</p>.*")
|
||||
template_page = urllib2.urlopen(url).read().replace('\n', '')
|
||||
parsed_template = version_regex.match(template_page)
|
||||
return parsed_template.groups()[0]
|
||||
|
||||
# Grab the release date, the beta version number
|
||||
release_date = getTemplateValue("https://wiki.mozilla.org/Template:FIREFOX_SHIP_DATE")
|
||||
beta_version = getTemplateValue("https://wiki.mozilla.org/Template:BETA_VERSION")
|
||||
current_version = getTemplateValue("https://wiki.mozilla.org/Template:CURRENT_VERSION")
|
||||
today = datetime.date.today()
|
||||
release = datetime.datetime.strptime(release_date, "%B %d, %Y").date()
|
||||
|
||||
# Check the timedelta between today and releasedate and if:
|
||||
# -7 days before release date Sign Off reminder for 'tomorrow': Thurs at 10am PT
|
||||
# -29 days before next release date send Post-Mortem for the previous version 'tomorrow': Tues at 10am PT)
|
||||
timedelta = today - release
|
||||
|
||||
if timedelta.days == -7:
|
||||
# send the reminder email for sign off meeting
|
||||
print "Sending Sign-off email reminder %s" % today
|
||||
subject = "Automatic Reminder: Firefox %s Sign Off Meeting" % beta_version
|
||||
body = """
|
||||
This is a reminder that the FF%s sign-off meeting will be held tomorrow in the Release Coordination Vidyo room @ 10:00 am PT.
|
||||
|
||||
The wiki page is up and ready for you to add notes : https://wiki.mozilla.org/Releases/Firefox_%s/Final_Signoffs
|
||||
|
||||
-- Release Management
|
||||
""" % (beta_version, beta_version)
|
||||
if timedelta.days == -29:
|
||||
# send the reminder email for post-mortem of curent release version
|
||||
print "Sending post-mortem email reminder %s" % today
|
||||
subject = "Reminder: Firefox %s Post Mortem Meeting Tomorrow" % current_version
|
||||
body = """
|
||||
Friendly Reminder that the FF%s.0 Post-Mortem will take place tomorrow @ 10:00 am PT during the Channel Meeting in the Release Co-ordination Vidyo room.
|
||||
|
||||
Etherpad - https://etherpad.mozilla.org/%s-0-Post-Mortem
|
||||
|
||||
-- Release Management
|
||||
""" % (current_version, current_version)
|
||||
|
||||
if subject is not None:
|
||||
options = {
|
||||
"username": config['ldap_username'],
|
||||
"password": config['ldap_password'],
|
||||
"subject": subject,
|
||||
"body": body,
|
||||
"cclist": "release-mgmt@mozilla.com",
|
||||
"toaddrs": toaddrs
|
||||
}
|
||||
for email in toaddrs:
|
||||
sendMail(email, options)
|
||||
else:
|
||||
print "No command today: %s" % today
|
|
@ -1,138 +0,0 @@
|
|||
#!/usr/bin/python
|
||||
|
||||
import datetime
|
||||
import subprocess
|
||||
import os
|
||||
import json
|
||||
from argparse import ArgumentParser
|
||||
|
||||
CONFIG_JSON = os.getcwd() + "/scripts/configs/config.json"
|
||||
config = json.load(open(CONFIG_JSON, 'r'))
|
||||
scripts_dir = os.getcwd() + "/scripts/"
|
||||
queries_dir = os.getcwd() + "/queries/"
|
||||
|
||||
|
||||
def createQuery(title, short_title, url, show_summary, cc):
|
||||
file_name = queries_dir + str(datetime.date.today()) + '_' + short_title
|
||||
|
||||
qf = open(file_name, 'w')
|
||||
qf.write("query_name = \'" + title + "\'\n")
|
||||
qf.write("query_url = \'" + url + "\'\n")
|
||||
qf.write("show_summary = \'" + str(show_summary) + "\'\n")
|
||||
if cc is not None:
|
||||
qf.write("cc = \'" + ",".join(cc) + "\'\n")
|
||||
|
||||
return file_name
|
||||
|
||||
|
||||
def createQueriesList(print_all):
|
||||
queries = []
|
||||
weekday = datetime.datetime.today().weekday()
|
||||
for url in urls:
|
||||
try:
|
||||
cc = url[1][4]
|
||||
except IndexError:
|
||||
cc = None # no cc
|
||||
# Every Weekday
|
||||
if weekday >= 0 and weekday < 5 and url[0] == 5:
|
||||
queries.append(createQuery(title=url[1][0], short_title=url[1][1], url=url[1][2], show_summary=url[1][3], cc=cc))
|
||||
# Monday only emails
|
||||
if weekday == 0 and url[0] == 0:
|
||||
queries.append(createQuery(title=url[1][0], short_title=url[1][1], url=url[1][2], show_summary=url[1][3], cc=cc))
|
||||
# Tuesday only emails
|
||||
if weekday == 1 and url[0] == 1:
|
||||
queries.append(createQuery(title=url[1][0], short_title=url[1][1], url=url[1][2], show_summary=url[1][3], cc=cc))
|
||||
# Thursday only emails
|
||||
if weekday == 3 and url[0] == 3:
|
||||
queries.append(createQuery(title=url[1][0], short_title=url[1][1], url=url[1][2], show_summary=url[1][3], cc=cc))
|
||||
# Friday only emails
|
||||
if weekday == 4 and url[0] == 4:
|
||||
queries.append(createQuery(title=url[1][0], short_title=url[1][1], url=url[1][2], show_summary=url[1][3], cc=cc))
|
||||
return queries
|
||||
|
||||
# ========================= CURRENT QUERIES ============================
|
||||
# NOTE: You must replace query bug_status with comma-separated values
|
||||
# eg: &bug_status=UNCONFIRMED,NEW,ASSIGNED,REOPENED
|
||||
#
|
||||
# ==== KOI (v1.2) ====
|
||||
unfixed_koi_sec_url = "https://bugzilla.mozilla.org/buglist.cgi?f1=cf_blocking_b2g&o1=equals&resolution=---&bug_status=UNCONFIRMED,NEW,READY,ASSIGNED,REOPENED&v1=koi%2B&f2=status_whiteboard&o2=notsubstring&v2=[no-nag]&o3=anywordssubstr&v3=Confidential%20Security&f3=bug_group"
|
||||
unfixed_koi_url = "https://bugzilla.mozilla.org/buglist.cgi?f1=cf_blocking_b2g&o1=equals&resolution=---&bug_status=UNCONFIRMED,NEW,READY,ASSIGNED,REOPENED&v1=koi%2B&f2=status_whiteboard&o2=notsubstring&v2=[no-nag]&o3=anywordssubstr&v3=Confidential%20Security&f3=bug_group&n3=1"
|
||||
|
||||
team_dev_koi_nom = "https://bugzilla.mozilla.org/buglist.cgi?f1=OP&f0=OP&o2=equals&f4=CP&j1=OR&f3=CP&f2=cf_blocking_b2g&bug_status=UNCONFIRMED,NEW,ASSIGNED,REOPENED&component=DOM%3A%20Device%20Interfaces&v2=koi%3F&product=Core&o5=equals&n5=1&v5=FIXED&f5=resolution"
|
||||
team_dev_koi_blockers = "https://bugzilla.mozilla.org/buglist.cgi?f1=OP&f0=OP&o2=equals&f4=CP&j1=OR&f3=CP&f2=cf_blocking_b2g&bug_status=UNCONFIRMED,NEW,ASSIGNED,REOPENED&component=DOM%3A%20Device%20Interfaces&v2=koi%2B&product=Core&o5=equals&n5=1&v5=FIXED&f5=resolution&o6=equals&n6=1&v6=WORKSFORME&f6=resolution&o7=equals&n7=1&v7=DUPLICATE&f7=resolution&o8=equals&n8=1&v8=INVALID&f8=resolution&o9=equals&n9=1&v9=WONTFIX&f9=resolution"
|
||||
|
||||
team_gfx_koi_nom = "https://bugzilla.mozilla.org/buglist.cgi?f1=OP&f0=OP&o2=equals&f4=CP&j1=OR&f3=CP&f2=cf_blocking_b2g&bug_status=UNCONFIRMED,NEW,ASSIGNED,REOPENED&component=Graphics%3A%20Layers&v2=koi%3F&product=Core&o5=equals&n5=1&v5=FIXED&f5=resolution"
|
||||
team_gfx_koi_blockers = "https://bugzilla.mozilla.org/buglist.cgi?f1=OP&f0=OP&o2=equals&f4=CP&j1=OR&f3=CP&f2=cf_blocking_b2g&bug_status=UNCONFIRMED,NEW,ASSIGNED,REOPENED&component=Graphics%3A%20Layers&v2=koi%2B&product=Core&o5=equals&n5=1&v5=FIXED&f5=resolution&o6=equals&n6=1&v6=WORKSFORME&f6=resolution&o7=equals&n7=1&v7=DUPLICATE&f7=resolution&o8=equals&n8=1&v8=INVALID&f8=resolution&o9=equals&n9=1&v9=WONTFIX&f9=resolution"
|
||||
|
||||
team_media_koi_nom = "https://bugzilla.mozilla.org/buglist.cgi?f1=OP&f0=OP&o2=equals&f4=CP&j1=OR&f3=CP&f2=cf_blocking_b2g&bug_status=UNCONFIRMED,NEW,ASSIGNED,REOPENED&component=Gaia%3A%3ACamera&component=Gaia%3A%3AFMRadio&component=Gaia%3A%3AMusic&component=Gaia%3A%3AVideo&v2=koi%3F&product=Firefox%20OS&o5=equals&n5=1&v5=FIXED&f5=resolution&o6=equals&n6=1&v6=WORKSFORME&f6=resolution&o7=equals&n7=1&v7=DUPLICATE&f7=resolution&o8=equals&n8=1&v8=INVALID&f8=resolution&o9=equals&n9=1&v9=WONTFIX&f9=resolution"
|
||||
team_media_koi_blockers = "https://bugzilla.mozilla.org/buglist.cgi?f1=OP&f0=OP&o2=equals&f4=CP&j1=OR&f3=CP&f2=cf_blocking_b2g&bug_status=UNCONFIRMED,NEW,ASSIGNED,REOPENED&component=Gaia%3A%3ACamera&component=Gaia%3A%3AFMRadio&component=Gaia%3A%3AMusic&component=Gaia%3A%3AVideo&v2=koi%2B&product=Firefox%20OS&o5=equals&n5=1&v5=FIXED&f5=resolution&o6=equals&n6=1&v6=WORKSFORME&f6=resolution&o7=equals&n7=1&v7=DUPLICATE&f7=resolution&o8=equals&n8=1&v8=INVALID&f8=resolution&o9=equals&n9=1&v9=WONTFIX&f9=resolution"
|
||||
|
||||
team_comm_koi_nom = "https://bugzilla.mozilla.org/buglist.cgi?f1=OP&f0=OP&o2=equals&f4=CP&j1=OR&f3=CP&f2=cf_blocking_b2g&bug_status=UNCONFIRMED,NEW,ASSIGNED,REOPENED&component=Gaia%3A%3AContacts&component=Gaia%3A%3ADialer&component=Gaia%3A%3AEverything.me&component=Gaia%3A%3ASMS&v2=koi%3F&product=Firefox%20OS&o5=equals&n5=1&v5=FIXED&f5=resolution"
|
||||
team_comm_koi_blockers = "https://bugzilla.mozilla.org/buglist.cgi?f1=OP&f0=OP&o2=equals&f4=CP&j1=OR&f3=CP&f2=cf_blocking_b2g&bug_status=UNCONFIRMED,NEW,ASSIGNED,REOPENED&component=Gaia%3A%3AContacts&component=Gaia%3A%3ADialer&component=Gaia%3A%3AEverything.me&component=Gaia%3A%3ASMS&v2=koi%2B&product=Firefox%20OS&o5=equals&n5=1&v5=FIXED&f5=resolution&o6=equals&n6=1&v6=WORKSFORME&f6=resolution&o7=equals&n7=1&v7=DUPLICATE&f7=resolution&o8=equals&n8=1&v8=INVALID&f8=resolution&o9=equals&n9=1&v9=WONTFIX&f9=resolution"
|
||||
|
||||
koi_regressions_unfixed = "https://bugzilla.mozilla.org/buglist.cgi?j_top=OR&keywords=regression%2C%20regressionwindow-wanted%2C%20&keywords_type=anywords&f1=cf_blocking_b2g&o1=anywords&bug_status=UNCONFIRMED&bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED&v1=koi"
|
||||
koi_assignee_no_comment_three_days = "https://bugzilla.mozilla.org/buglist.cgi?o5=nowordssubstr&f1=OP&f0=OP&f8=owner_idle_time&o2=equals&f4=OP&v5=fixed%20verified%20unaffected%20wontfix&j1=OR&f3=CP&f2=cf_blocking_b2g&bug_status=UNCONFIRMED&bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED&j4=OR&f5=cf_status_b2g_1_2&v8=-3d&f6=CP&v2=koi%2B&f7=CP&o8=greaterthan"
|
||||
|
||||
# ==== General ====
|
||||
needinfo_sec_blockers_url = "https://bugzilla.mozilla.org/buglist.cgi?o1=anywords&o2=substring&v1=koi%2B&v2=needinfo%3F&f1=cf_blocking_b2g&resolution=---&f2=flagtypes.name&f3=status_whiteboard&o3=notsubstring&v3=[no-nag]&o4=anywordssubstr&v4=Confidential%20Security&f4=bug_group"
|
||||
needinfo_blockers_url = "https://bugzilla.mozilla.org/buglist.cgi?o1=anywords&o2=substring&v1=koi%2B&v2=needinfo%3F&f1=cf_blocking_b2g&resolution=---&f2=flagtypes.name&f3=status_whiteboard&o3=notsubstring&v3=[no-nag]&o4=anywordssubstr&v4=Confidential%20Security&f4=bug_group&n4=1"
|
||||
|
||||
# TODO - sort the queries according to a priority flag
|
||||
# TODO - batch up by query name (so sec & non-sec get in the same output)
|
||||
urls = [
|
||||
(5, ["Blocker Bugs with Need-Info? (Sec)", "needinfo_sec_blockers", needinfo_sec_blockers_url, 0]),
|
||||
(5, ["Blocker Bugs with Need-Info?", "needinfo_blockers", needinfo_blockers_url, 1]),
|
||||
(0, ["Bugs Blocking KOI (Sec)", "unfixed_koi_sec", unfixed_koi_sec_url, 0]),
|
||||
(0, ["Bugs Blocking KOI", "unfixed_koi", unfixed_koi_url, 1]),
|
||||
(1, ["Koi Blocking Nominiations for DOM: Dev Interfaces", "team_dev_koi_nom", team_dev_koi_nom, 1, ["dhylands@mozilla.com", ]]),
|
||||
(1, ["Koi Blocker Bugs, DOM: Dev Interfaces", "team_dev_koi_blockers", team_dev_koi_blockers, 1, ["dhylands@mozilla.com", ]]),
|
||||
(1, ["Koi Blocking Nominiations for Graphics", "team_gfx_koi_nom", team_gfx_koi_nom, 1, ["msreckovic@mozilla.com", ]]),
|
||||
(1, ["Koi Blocker Bugs, Graphics", "team_gfx_koi_blockers", team_gfx_koi_blockers, 1, ["msreckovic@mozilla.com", ]]),
|
||||
(1, ["Koi Blocking Nominiations for B2G: Media", "team_media_koi_nom", team_media_koi_nom, 1, ["hkoka@mozilla.com", ]]),
|
||||
(1, ["Koi Blocker Bugs, B2G: Media", "team_media_koi_blockers", team_media_koi_blockers, 1, ["hkoka@mozilla.com", ]]),
|
||||
(1, ["Koi Blocking Nominiations for B2G: Communications", "team_comm_koi_nom", team_comm_koi_nom, 1, ["dscravaglieri@mozilla.com", ]]),
|
||||
(1, ["Koi Blocker Bugs, B2G: Communications", "team_comm_koi_blockers", team_comm_koi_blockers, 1, ["dscravaglieri@mozilla.com", ]]),
|
||||
(4, ["Koi Blocker Bugs, No Comment from Assignee in 3 days or more", "koi_assignee_no_comment_three_days", koi_assignee_no_comment_three_days, 1]),
|
||||
(4, ["Koi Blocker Bugs, Regressions", "koi_regressions_unfixed", koi_regressions_unfixed, 1]),
|
||||
]
|
||||
|
||||
|
||||
def cleanUp():
|
||||
for file in os.listdir(queries_dir):
|
||||
if file.startswith(str(datetime.date.today())):
|
||||
os.remove(os.path.join(queries_dir, file))
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = ArgumentParser(__doc__)
|
||||
parser.set_defaults(
|
||||
queries_only=False,
|
||||
)
|
||||
parser.add_argument("-q", "--queries-only", dest="queries_only", action="store_true",
|
||||
help="just create and print queries")
|
||||
|
||||
options, args = parser.parse_known_args()
|
||||
queries = createQueriesList(print_all=options.queries_only)
|
||||
|
||||
if options.queries_only:
|
||||
for url in urls:
|
||||
print url
|
||||
else:
|
||||
command = [
|
||||
scripts_dir + "email_nag.py",
|
||||
"-t", "daily_b2g_email",
|
||||
"--no-verification",
|
||||
"-m", config['ldap_username'],
|
||||
"-p", config['ldap_password']]
|
||||
for query in queries:
|
||||
command.append('-q')
|
||||
command.append(query)
|
||||
if ('-r') in args:
|
||||
subject = datetime.datetime.today().strftime("%A %b %d") + " -- Daily B2G Drivers Rollup"
|
||||
command.extend(['-e', 'b2g-release-drivers@mozilla.org'])
|
||||
else:
|
||||
subject = datetime.datetime.today().strftime("%A %b %d") + " -- Daily B2G Blocking Bugs Alert"
|
||||
command.extend(['-s', subject])
|
||||
# send all other args to email_nag script argparser
|
||||
command.extend(args)
|
||||
subprocess.call(command)
|
||||
cleanUp()
|
|
@ -1,474 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
"""
|
||||
|
||||
A script for automated nagging emails based on passed in queries
|
||||
These can be collated into several 'queries' through the use of multiple query files with
|
||||
a 'query_name' param set eg: 'Bugs tracked for Firefox Beta (13)'
|
||||
Once the bugs have been collected from Bugzilla they are sorted into buckets cc: assignee manager
|
||||
and to the assignee(s) or need-info? for each query
|
||||
|
||||
"""
|
||||
import sys
|
||||
import os
|
||||
import smtplib
|
||||
import subprocess
|
||||
import tempfile
|
||||
import collections
|
||||
from datetime import datetime
|
||||
from argparse import ArgumentParser
|
||||
from bugzilla.agents import BMOAgent
|
||||
import phonebook
|
||||
from jinja2 import Environment, FileSystemLoader
|
||||
env = Environment(loader=FileSystemLoader('templates'))
|
||||
|
||||
REPLY_TO_EMAIL = 'release-mgmt@mozilla.com'
|
||||
DEFAULT_CC = ['release-mgmt@mozilla.com']
|
||||
EMAIL_SUBJECT = ''
|
||||
SMTP = 'smtp.mozilla.org'
|
||||
people = phonebook.PhonebookDirectory()
|
||||
|
||||
# TODO - Sort by who a bug is blocked on (thanks @dturner)
|
||||
# TODO - write tests!
|
||||
# TODO - look into knocking out duplicated bugs in queries -- perhaps print out if there are dupes in queries when queries > 1
|
||||
# TODO - should compare bugmail from API results to phonebook bugmail in to_lower()
|
||||
|
||||
|
||||
def get_last_manager_comment(comments, manager):
|
||||
# go through in reverse order to get most recent
|
||||
for comment in comments[::-1]:
|
||||
if person is not None:
|
||||
if comment.creator.name == manager['mozillaMail'] or comment.creator.name == manager['bugzillaEmail']:
|
||||
return comment.creation_time.replace(tzinfo=None)
|
||||
return None
|
||||
|
||||
|
||||
def get_last_assignee_comment(comments, person):
|
||||
# go through in reverse order to get most recent
|
||||
for comment in comments[::-1]:
|
||||
if person is not None:
|
||||
if comment.creator.name == person['mozillaMail'] or comment.creator.name == person['bugzillaEmail']:
|
||||
return comment.creation_time.replace(tzinfo=None)
|
||||
return None
|
||||
|
||||
|
||||
def query_url_to_dict(url):
|
||||
if (';')in url:
|
||||
fields_and_values = url.split("?")[1].split(";")
|
||||
else:
|
||||
fields_and_values = url.split("?")[1].split("&")
|
||||
d = collections.defaultdict(list)
|
||||
|
||||
for pair in fields_and_values:
|
||||
(key, val) = pair.split("=")
|
||||
if key != "list_id":
|
||||
d[key].append(val)
|
||||
return d
|
||||
|
||||
|
||||
def generateEmailOutput(subject, queries, template, show_comment=False, manager_email=None, rollup=False, rollupEmail=None):
|
||||
cclist = []
|
||||
toaddrs = []
|
||||
template_params = {}
|
||||
# stripping off the templates dir, just in case it gets passed in the args
|
||||
template = env.get_template(template.replace('templates/', '', 1))
|
||||
|
||||
def addToAddrs(bug):
|
||||
if bug.assigned_to.name in people.people_by_bzmail:
|
||||
person = dict(people.people_by_bzmail[bug.assigned_to.name])
|
||||
if person['mozillaMail'] not in toaddrs:
|
||||
toaddrs.append(person['mozillaMail'])
|
||||
|
||||
for query in queries.keys():
|
||||
# Avoid dupes in the cclist from several queries
|
||||
query_cc = queries[query].get('cclist', [])
|
||||
for qcc in query_cc:
|
||||
if qcc not in cclist:
|
||||
cclist.append(qcc)
|
||||
if query not in template_params:
|
||||
template_params[query] = {'buglist': []}
|
||||
if len(queries[query]['bugs']) != 0:
|
||||
for bug in queries[query]['bugs']:
|
||||
if 'show_summary' in queries[query]:
|
||||
if queries[query]['show_summary'] == '1':
|
||||
summary = bug.summary
|
||||
else:
|
||||
summary = ""
|
||||
else:
|
||||
summary = ""
|
||||
template_params[query]['buglist'].append(
|
||||
{
|
||||
'id': bug.id,
|
||||
'summary': summary,
|
||||
# 'comment': bug.comments[-1].creation_time.replace(tzinfo=None),
|
||||
'assignee': bug.assigned_to.real_name,
|
||||
'flags': bug.flags
|
||||
}
|
||||
)
|
||||
# more hacking for JS special casing
|
||||
if bug.assigned_to.name == 'general@js.bugs' and 'nihsanullah@mozilla.com' not in toaddrs:
|
||||
toaddrs.append('nihsanullah@mozilla.com')
|
||||
# if needinfo? in flags, add the flag.requestee to the toaddrs instead of bug assignee
|
||||
if bug.flags:
|
||||
for flag in bug.flags:
|
||||
if 'needinfo' in flag.name and flag.status == '?':
|
||||
try:
|
||||
person = dict(people.people_by_bzmail[str(flag.requestee)])
|
||||
if person['mozillaMail'] not in toaddrs:
|
||||
toaddrs.append(person['mozillaMail'])
|
||||
except:
|
||||
if str(flag.requestee) not in toaddrs:
|
||||
toaddrs.append(str(flag.requestee))
|
||||
else:
|
||||
addToAddrs(bug)
|
||||
else:
|
||||
addToAddrs(bug)
|
||||
|
||||
message_body = template.render(queries=template_params, show_comment=show_comment)
|
||||
if manager_email is not None and manager_email not in cclist:
|
||||
cclist.append(manager_email)
|
||||
# no need to and cc the manager if more than one email
|
||||
if len(toaddrs) > 1:
|
||||
for email in toaddrs:
|
||||
if email in cclist:
|
||||
toaddrs.remove(email)
|
||||
|
||||
if cclist == ['']:
|
||||
cclist = None
|
||||
if rollup:
|
||||
joined_to = ",".join(rollupEmail)
|
||||
else:
|
||||
joined_to = ",".join(toaddrs)
|
||||
message = (
|
||||
"From: %s\r\n" % REPLY_TO_EMAIL
|
||||
+ "To: %s\r\n" % joined_to
|
||||
+ "CC: %s\r\n" % ",".join(cclist)
|
||||
+ "Subject: %s\r\n" % subject
|
||||
+ "\r\n"
|
||||
+ message_body)
|
||||
|
||||
toaddrs = toaddrs + cclist
|
||||
|
||||
return toaddrs, message
|
||||
|
||||
|
||||
def sendMail(toaddrs, msg, username, password, dryrun=False):
|
||||
if dryrun:
|
||||
print "\n****************************\n* DRYRUN: not sending mail *\n****************************\n"
|
||||
print msg
|
||||
else:
|
||||
server = smtplib.SMTP_SSL(SMTP, 465)
|
||||
server.set_debuglevel(1)
|
||||
server.login(username, password)
|
||||
# note: toaddrs is required for transport agents, the msg['To'] header is not modified
|
||||
server.sendmail(username, toaddrs, msg)
|
||||
server.quit()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = ArgumentParser(__doc__)
|
||||
parser.set_defaults(
|
||||
dryrun=False,
|
||||
username=None,
|
||||
password=None,
|
||||
roll_up=False,
|
||||
show_comment=False,
|
||||
email_cc_list=None,
|
||||
queries=[],
|
||||
days_since_comment=-1,
|
||||
verbose=False,
|
||||
keywords=None,
|
||||
email_subject=None,
|
||||
no_verification=False,
|
||||
)
|
||||
parser.add_argument("-d", "--dryrun", dest="dryrun", action="store_true",
|
||||
help="just do the query, and print emails to console without emailing anyone")
|
||||
parser.add_argument("-m", "--mozilla-email", dest="mozilla_mail",
|
||||
help="specify a specific address for sending email"),
|
||||
parser.add_argument("-p", "--email-password", dest="email_password",
|
||||
help="specify a specific password for sending email")
|
||||
parser.add_argument("-b", "--bz-api-key", dest="bz_api_key",
|
||||
help="Bugzilla API key")
|
||||
parser.add_argument("-t", "--template", dest="template",
|
||||
required=True,
|
||||
help="template to use for the buglist output")
|
||||
parser.add_argument("-e", "--email-cc-list", dest="email_cc_list",
|
||||
action="append",
|
||||
help="email addresses to include in cc when sending mail")
|
||||
parser.add_argument("-q", "--query", dest="queries",
|
||||
action="append",
|
||||
required=True,
|
||||
help="a file containing a dictionary of a bugzilla query")
|
||||
parser.add_argument("-k", "--keyword", dest="keywords",
|
||||
action="append",
|
||||
help="keywords to collate buglists")
|
||||
parser.add_argument("-s", "--subject", dest="email_subject",
|
||||
required=True,
|
||||
help="The subject of the email being sent")
|
||||
parser.add_argument("-r", "--roll-up", dest="roll_up", action="store_true",
|
||||
help="flag to get roll-up output in one email instead of creating multiple emails")
|
||||
parser.add_argument("--show-comment", dest="show_comment", action="store_true",
|
||||
help="flag to display last comment on a bug in the message output")
|
||||
parser.add_argument("--days-since-comment", dest="days_since_comment",
|
||||
help="threshold to check comments against to take action based on days since comment")
|
||||
parser.add_argument("--verbose", dest="verbose", action="store_true",
|
||||
help="turn on verbose output")
|
||||
parser.add_argument("--no-verification", dest="no_verification", action="store_true",
|
||||
help="don't wait for human verification of every email")
|
||||
|
||||
options, args = parser.parse_known_args()
|
||||
|
||||
try:
|
||||
int(options.days_since_comment)
|
||||
except:
|
||||
if options.days_since_comment is not None:
|
||||
parser.error("Need to provide a number for days \
|
||||
since last comment value")
|
||||
if options.email_cc_list is None:
|
||||
options.email_cc_list = DEFAULT_CC
|
||||
|
||||
# Load our agent for BMO
|
||||
bmo = BMOAgent(options.bz_api_key)
|
||||
|
||||
# Get the buglist(s)
|
||||
collected_queries = {}
|
||||
for query in options.queries:
|
||||
# import the query
|
||||
if os.path.exists(query):
|
||||
info = {}
|
||||
execfile(query, info)
|
||||
query_name = info['query_name']
|
||||
if query_name not in collected_queries:
|
||||
collected_queries[query_name] = {
|
||||
'channel': info.get('query_channel', ''),
|
||||
'bugs': [],
|
||||
'show_summary': info.get('show_summary', 0),
|
||||
'cclist': options.email_cc_list,
|
||||
}
|
||||
if 'cc' in info:
|
||||
for c in info.get('cc').split(','):
|
||||
collected_queries[query_name]['cclist'].append(c)
|
||||
if 'query_params' in info:
|
||||
print "Gathering bugs from query_params in %s" % query
|
||||
collected_queries[query_name]['bugs'] = bmo.get_bug_list(info['query_params'])
|
||||
elif 'query_url' in info:
|
||||
print "Gathering bugs from query_url in %s" % query
|
||||
collected_queries[query_name]['bugs'] = bmo.get_bug_list(query_url_to_dict(info['query_url']))
|
||||
# print "DEBUG: %d bug(s) found for query %s" % \
|
||||
# (len(collected_queries[query_name]['bugs']), info['query_url'])
|
||||
else:
|
||||
print "Error - no valid query params or url in the config file"
|
||||
sys.exit(1)
|
||||
else:
|
||||
print "Not a valid path: %s" % query
|
||||
total_bugs = 0
|
||||
for channel in collected_queries.keys():
|
||||
total_bugs += len(collected_queries[channel]['bugs'])
|
||||
|
||||
print "Found %s bugs total for %s queries" % (total_bugs, len(collected_queries.keys()))
|
||||
print "Queries to collect: %s" % collected_queries.keys()
|
||||
|
||||
managers = people.managers
|
||||
manual_notify = {}
|
||||
counter = 0
|
||||
|
||||
def add_to_managers(manager_email, query, info={}):
|
||||
if manager_email not in managers:
|
||||
managers[manager_email] = {}
|
||||
managers[manager_email]['nagging'] = {query: {'bugs': [bug],
|
||||
'show_summary': info.get('show_summary', 0),
|
||||
'cclist': info.get('cclist', [])}, }
|
||||
return
|
||||
if 'nagging' in managers[manager_email]:
|
||||
if query in managers[manager_email]['nagging']:
|
||||
managers[manager_email]['nagging'][query]['bugs'].append(bug)
|
||||
if options.verbose:
|
||||
print "Adding %s to %s in nagging for %s" % \
|
||||
(bug.id, query, manager_email)
|
||||
else:
|
||||
managers[manager_email]['nagging'][query] = {
|
||||
'bugs': [bug],
|
||||
'show_summary': info.get('show_summary', 0),
|
||||
'cclist': info.get('cclist', [])
|
||||
}
|
||||
if options.verbose:
|
||||
print "Adding new query key %s for bug %s in nagging \
|
||||
and %s" % (query, bug.id, manager_email)
|
||||
else:
|
||||
managers[manager_email]['nagging'] = {query: {'bugs': [bug],
|
||||
'show_summary': info.get('show_summary', 0),
|
||||
'cclist': info.get('cclist', [])}, }
|
||||
if options.verbose:
|
||||
print "Creating query key %s for bug %s in nagging and \
|
||||
%s" % (query, bug.id, manager_email)
|
||||
|
||||
for query, info in collected_queries.items():
|
||||
if len(collected_queries[query]['bugs']) != 0:
|
||||
manual_notify[query] = {'bugs': [], 'show_summary': info.get('show_summary', 0)}
|
||||
for b in collected_queries[query]['bugs']:
|
||||
counter = counter + 1
|
||||
send_mail = True
|
||||
bug = bmo.get_bug(b.id)
|
||||
manual_notify[query]['bugs'].append(bug)
|
||||
assignee = bug.assigned_to.name
|
||||
if "@" not in assignee:
|
||||
print "Error - email address expect. Found '" + assignee + "' instead"
|
||||
print "Check that the authentication worked correctly"
|
||||
sys.exit(1)
|
||||
if assignee in people.people_by_bzmail:
|
||||
person = dict(people.people_by_bzmail[assignee])
|
||||
else:
|
||||
person = None
|
||||
|
||||
# kick bug out if days since comment check is on
|
||||
if options.days_since_comment != -1:
|
||||
# try to get last_comment by assignee & manager
|
||||
if person is not None:
|
||||
last_comment = get_last_assignee_comment(bug.comments, person)
|
||||
if 'manager' in person and person['manager'] is not None:
|
||||
manager_email = person['manager']['dn'].split('mail=')[1].split(',')[0]
|
||||
manager = people.people[manager_email]
|
||||
last_manager_comment = get_last_manager_comment(bug.comments, people.people_by_bzmail[manager['bugzillaEmail']])
|
||||
# set last_comment to the most recent of last_assignee and last_manager
|
||||
if last_manager_comment is not None and last_comment is not None and last_manager_comment > last_comment:
|
||||
last_comment = last_manager_comment
|
||||
# otherwise just get the last comment
|
||||
else:
|
||||
last_comment = bug.comments[-1].creation_time.replace(tzinfo=None)
|
||||
if last_comment is not None:
|
||||
timedelta = datetime.now() - last_comment
|
||||
if timedelta.days <= int(options.days_since_comment):
|
||||
if options.verbose:
|
||||
print "Skipping bug %s since it's had an assignee or manager comment within the past %s days" % (bug.id, options.days_since_comment)
|
||||
send_mail = False
|
||||
counter = counter - 1
|
||||
manual_notify[query]['bugs'].remove(bug)
|
||||
else:
|
||||
if options.verbose:
|
||||
print "This bug needs notification, it's been %s since last comment of note" % timedelta.days
|
||||
|
||||
if send_mail:
|
||||
if 'nobody' in assignee:
|
||||
if options.verbose:
|
||||
print "No one assigned to: %s, will be in the manual notification list..." % bug.id
|
||||
# TODO - get rid of this, SUCH A HACK!
|
||||
elif 'general@js.bugs' in assignee:
|
||||
if options.verbose:
|
||||
print "No one assigned to JS bug: %s, adding to Naveed's list..." % bug.id
|
||||
add_to_managers('nihsanullah@mozilla.com', query, info)
|
||||
else:
|
||||
if bug.assigned_to.real_name is not None:
|
||||
if person is not None:
|
||||
# check if assignee is already a manager, add to their own list
|
||||
if 'mozillaMail' in managers:
|
||||
add_to_managers(person['mozillaMail'], query, info)
|
||||
# otherwise we search for the assignee's manager
|
||||
else:
|
||||
# check for manager key first, a few people don't have them
|
||||
if 'manager' in person and person['manager'] is not None:
|
||||
manager_email = person['manager']['dn'].split('mail=')[1].split(',')[0]
|
||||
if manager_email in managers:
|
||||
add_to_managers(manager_email, query, info)
|
||||
elif manager_email in people.vices:
|
||||
# we're already at the highest level we'll go
|
||||
if assignee in managers:
|
||||
add_to_managers(assignee, query, info)
|
||||
else:
|
||||
if options.verbose:
|
||||
print "%s has a V-level for a manager, and is not in the manager list" % assignee
|
||||
managers[person['mozillaMail']] = {}
|
||||
add_to_managers(person['mozillaMail'], query, info)
|
||||
else:
|
||||
# try to go up one level and see if we find a manager
|
||||
if manager_email in people.people:
|
||||
person = dict(people.people[manager_email])
|
||||
manager_email = person['manager']['dn'].split('mail=')[1].split(',')[0]
|
||||
if manager_email in managers:
|
||||
add_to_managers(manager_email, query, info)
|
||||
else:
|
||||
print "Manager could not be found: %s" % manager_email
|
||||
# if you don't have a manager listed, but are an employee, we'll nag you anyway
|
||||
else:
|
||||
add_to_managers(person['mozillaMail'], query, info)
|
||||
print "%s's entry doesn't list a manager! Let's ask them to update phonebook but in the meantime they get the email directly." % person['name']
|
||||
|
||||
if options.roll_up:
|
||||
# only send one email
|
||||
toaddrs, msg = generateEmailOutput(subject=options.email_subject,
|
||||
queries=manual_notify,
|
||||
template=options.template,
|
||||
show_comment=options.show_comment,
|
||||
rollup=options.roll_up,
|
||||
rollupEmail=options.email_cc_list)
|
||||
if options.email_password is None or options.mozilla_mail is None:
|
||||
print "Please supply a username/password (-m, -p) for sending email"
|
||||
sys.exit(1)
|
||||
if not options.dryrun:
|
||||
print "SENDING EMAIL"
|
||||
sendMail(toaddrs, msg, options.mozilla_mail, options.email_password, options.dryrun)
|
||||
else:
|
||||
# Get yr nag on!
|
||||
for email, info in managers.items():
|
||||
inp = ''
|
||||
if 'nagging' in info:
|
||||
toaddrs, msg = generateEmailOutput(
|
||||
subject=options.email_subject,
|
||||
manager_email=email,
|
||||
queries=info['nagging'],
|
||||
template=options.template,
|
||||
show_comment=options.show_comment)
|
||||
while True and not options.no_verification:
|
||||
print "\nRelMan Nag is ready to send the following email:\n<------ MESSAGE BELOW -------->"
|
||||
print msg
|
||||
print "<------- END MESSAGE -------->\nWould you like to send now?"
|
||||
inp = raw_input('\n Please select y/Y to send, v/V to edit, or n/N to skip and continue to next email: ')
|
||||
|
||||
if inp != 'v' and inp != 'V':
|
||||
break
|
||||
|
||||
tempfilename = tempfile.mktemp()
|
||||
temp_file = open(tempfilename, 'w')
|
||||
temp_file.write(msg)
|
||||
temp_file.close()
|
||||
|
||||
subprocess.call(['vi', tempfilename])
|
||||
|
||||
temp_file = open(tempfilename, 'r')
|
||||
msg = temp_file.read()
|
||||
toaddrs = msg.split("To: ")[1].split("\r\n")[0].split(',') + msg.split("CC: ")[1].split("\r\n")[0].split(',')
|
||||
os.remove(tempfilename)
|
||||
|
||||
if inp == 'y' or inp == 'Y' or options.no_verification:
|
||||
if options.email_password is None or options.mozilla_mail is None:
|
||||
print "Please supply a username/password (-m, -p) for sending email"
|
||||
sys.exit(1)
|
||||
if not options.dryrun:
|
||||
print "SENDING EMAIL"
|
||||
sendMail(toaddrs, msg, options.mozilla_mail, options.email_password, options.dryrun)
|
||||
sent_bugs = 0
|
||||
for query, info in info['nagging'].items():
|
||||
sent_bugs += len(info['bugs'])
|
||||
# take sent bugs out of manual notification list
|
||||
for bug in info['bugs']:
|
||||
manual_notify[query]['bugs'].remove(bug)
|
||||
counter = counter - sent_bugs
|
||||
|
||||
if not options.roll_up:
|
||||
emailed_bugs = []
|
||||
# Send RelMan the manual notification list only when there are bugs that didn't go out
|
||||
msg_body = """\n******************************************\nNo nag emails were generated for these bugs because
|
||||
they are either assigned to no one or to non-employees (though ni? on non-employees will get nagged).
|
||||
\nYou will need to look at the following bugs:\n******************************************\n\n"""
|
||||
for k, v in manual_notify.items():
|
||||
if len(v['bugs']) != 0:
|
||||
for bug in v['bugs']:
|
||||
if bug.id not in emailed_bugs:
|
||||
if k not in msg_body:
|
||||
msg_body += "\n=== %s ===\n" % k
|
||||
emailed_bugs.append(bug.id)
|
||||
msg_body += "http://bugzil.la/" + "%s -- assigned to: %s\n -- Last commented on: %s\n" % (bug.id, bug.assigned_to.real_name, bug.comments[-1].creation_time.replace(tzinfo=None))
|
||||
msg = ("From: %s\r\n" % REPLY_TO_EMAIL
|
||||
+ "To: %s\r\n" % REPLY_TO_EMAIL
|
||||
+ "Subject: RelMan Attention Needed: %s\r\n" % options.email_subject
|
||||
+ "\r\n"
|
||||
+ msg_body)
|
||||
sendMail(['release-mgmt@mozilla.com'], msg, options.mozilla_mail, options.email_password, options.dryrun)
|
|
@ -1,77 +0,0 @@
|
|||
import requests
|
||||
import os
|
||||
import json
|
||||
|
||||
# NOTE: You must create a file for CONFIG_JSON with your LDAP auth in it like:
|
||||
# {
|
||||
# "username": "username",
|
||||
# "password": "password"
|
||||
# }
|
||||
#
|
||||
# In order to access the phonebook data
|
||||
|
||||
|
||||
MY_DIR = os.path.abspath(os.path.dirname(__file__))
|
||||
PEOPLE_FILENAME = os.path.join(MY_DIR, 'people.json')
|
||||
CONFIG_JSON = os.path.join(os.path.dirname(__file__), "configs/config.json")
|
||||
BASE_URL = 'https://phonebook.mozilla.org'
|
||||
PEOPLE_URL = '%s/search.php?query=*&format=fligtar' % BASE_URL
|
||||
|
||||
'''
|
||||
a single phonebook entry data looks like this when you pull it from JSON:
|
||||
'email' = {
|
||||
ims : [],
|
||||
name : 'name',
|
||||
title : 'title',
|
||||
phones : 'string of numbers & assignments',
|
||||
ext : XXX,
|
||||
manager : {u'dn': u'mail=manager@mozilla.com,o=com,dc=mozilla', u'cn': u'Manager Name'},
|
||||
bugzillaEmail : 'email@example.com'
|
||||
|
||||
## this script adds in:
|
||||
mozillaMail : 'email@mozilla.com'
|
||||
}
|
||||
'''
|
||||
|
||||
|
||||
class PhonebookDirectory():
|
||||
|
||||
def __init__(self, config=CONFIG_JSON):
|
||||
config = json.load(open(config, 'r'))
|
||||
print "Fetching people from phonebook..."
|
||||
self.people = json.loads(requests.get(PEOPLE_URL, auth=(config['ldap_username'], config['ldap_password'])).content)
|
||||
self.people_by_bzmail = self.get_people_by_bzmail()
|
||||
self.managers = self.get_managers()
|
||||
self.vices = self.get_vices()
|
||||
|
||||
def get_managers(self):
|
||||
managers = {}
|
||||
for email, info in self.people.items():
|
||||
if self.people[email]['title'] is not None:
|
||||
if 'director' in self.people[email]['title'].lower() or 'manager' in self.people[email]['title'].lower():
|
||||
managers[email] = info
|
||||
# HACK! don't have titles with manager/director or missing bugmail address
|
||||
if email in ('dtownsend@mozilla.com', 'dougt@mozilla.com', 'mfinkle@mozilla.com', 'bsmedberg@mozilla.com', 'blassey@mozilla.com') and email not in managers.keys():
|
||||
managers[email] = info
|
||||
return managers
|
||||
|
||||
def get_vices(self):
|
||||
vices = {}
|
||||
for email, info in self.people.items():
|
||||
if self.people[email]['title'] is not None:
|
||||
if 'vice' in self.people[email]['title'].lower():
|
||||
vices[email] = info
|
||||
return vices
|
||||
|
||||
def get_people_by_bzmail(self):
|
||||
temp = {}
|
||||
for email, info in self.people.items():
|
||||
# if someone doesn't have a bugzillaEmail set, we'll try their mozilla mail instead
|
||||
if info.get('bugzillaEmail'):
|
||||
temp[info['bugzillaEmail']] = dict(info.items())
|
||||
temp[info['bugzillaEmail']].update({'mozillaMail': email})
|
||||
else:
|
||||
temp[email] = dict(info.items())
|
||||
temp[email].update({'mozillaMail': email})
|
||||
|
||||
return temp
|
|
@ -1,146 +0,0 @@
|
|||
#!/usr/bin/python
|
||||
|
||||
import urllib2
|
||||
import re
|
||||
import datetime
|
||||
import subprocess
|
||||
import os
|
||||
import json
|
||||
from argparse import ArgumentParser
|
||||
|
||||
CONFIG_JSON = os.getcwd() + "/scripts/configs/config.json"
|
||||
config = json.load(open(CONFIG_JSON, 'r'))
|
||||
scripts_dir = os.getcwd() + "/scripts/"
|
||||
queries_dir = os.getcwd() + "/queries/"
|
||||
|
||||
|
||||
def getTemplateValue(url):
|
||||
version_regex = re.compile(".*<p>(.*)</p>.*")
|
||||
template_page = urllib2.urlopen(url).read().replace('\n', '')
|
||||
parsed_template = version_regex.match(template_page)
|
||||
return parsed_template.groups()[0]
|
||||
|
||||
|
||||
def getReportURL(approval_flag, span):
|
||||
a = urllib2.urlopen("https://bugzilla.mozilla.org/page.cgi?id=release_tracking_report.html&q=" + approval_flag + "%3A%2B%3A" + span + "%3A0%3Aand%3A")
|
||||
return a.url
|
||||
|
||||
no_nag = ";field3-1-0=status_whiteboard;type3-1-0=notsubstring;value3-1-0=[no-nag]"
|
||||
beta_version = getTemplateValue("https://wiki.mozilla.org/Template:BETA_VERSION")
|
||||
aurora_version = getTemplateValue("https://wiki.mozilla.org/Template:AURORA_VERSION")
|
||||
central_version = getTemplateValue("https://wiki.mozilla.org/Template:CENTRAL_VERSION")
|
||||
esr_version = getTemplateValue("https://wiki.mozilla.org/Template:ESR_VERSION")
|
||||
cycle_span = getTemplateValue("https://wiki.mozilla.org/Template:CURRENT_CYCLE")
|
||||
|
||||
unlanded_beta_url = getReportURL("approval-mozilla-beta", cycle_span) + ";field0-0-0=cf_status_firefox" + beta_version + ";type0-0-0=nowordssubstr;value0-0-0=unaffected,fixed,verified,wontfix,disabled" + ";field0-1-0=cf_tracking_firefox" + beta_version + ";type0-1-0=equals;value0-1-0=%2B;field0-2-0=status_whiteboard;type0-2-0=notsubstring;value0-2-0=[no-nag]"
|
||||
unlanded_aurora_url = getReportURL("approval-mozilla-aurora", cycle_span) + ";field0-0-0=cf_status_firefox" + aurora_version + ";type0-0-0=nowordssubstr;value0-0-0=unaffected,fixed,verified,wontfix,disabled" + ";field0-1-0=cf_tracking_firefox" + aurora_version + ";type0-1-0=equals;value0-1-0=%2B;field0-2-0=status_whiteboard;type0-2-0=notsubstring;value0-2-0=[no-nag]"
|
||||
unlanded_esr38_url = getReportURL("approval-mozilla-esr38", cycle_span) + ";field0-0-0=cf_status_firefox_esr" + esr_version + ";type0-0-0=nowordssubstr;value0-0-0=unaffected,fixed,verified,wontfix,disabled" + ";field0-1-0=cf_tracking_firefox_esr" + esr_version + ";type0-1-0=equals;value0-1-0=%2B;field0-2-0=status_whiteboard;type0-2-0=notsubstring;value0-2-0=[no-nag]"
|
||||
|
||||
tracking_beta_url = "https://bugzilla.mozilla.org/buglist.cgi?type1-0-0=equals;type0-1-0=notequals;type0-5-0=notequals;value0-5-0=disabled;value0-4-0=verified;field0-1-0=cf_status_firefox" + beta_version + ";field0-0-0=cf_tracking_firefox" + beta_version + ";field2-0-0=flagtypes.name;value0-3-0=unaffected;value0-6-0=verified%20disabled;value0-1-0=wontfix;field0-5-0=cf_status_firefox" + beta_version + ";type0-0-0=equals;value0-0-0=%2B;type0-2-0=notequals;negate1=1;field0-3-0=cf_status_firefox" + beta_version + ";type0-4-0=notequals;value2-0-0=approval-mozilla-beta%3F;field0-6-0=cf_status_firefox" + beta_version + ";type2-0-0=notsubstring;value0-2-0=fixed;type0-3-0=notequals;value1-0-0=core-security;field0-2-0=cf_status_firefox" + beta_version + ";field0-4-0=cf_status_firefox" + beta_version + ";type0-6-0=notequals;field1-0-0=bug_group;field2-1-0=status_whiteboard;type2-1-0=notsubstring;value2-1-0=[no-nag]"
|
||||
|
||||
tracking_aurora_url = "https://bugzilla.mozilla.org/buglist.cgi?type1-0-0=equals;type0-1-0=notequals;type0-5-0=notequals;value0-5-0=disabled;value0-4-0=verified;type3-0-0=notequals;field0-1-0=cf_status_firefox" + aurora_version + ";field0-0-0=cf_tracking_firefox" + aurora_version + ";field2-0-0=flagtypes.name;value0-3-0=unaffected;value0-6-0=verified%20disabled;value0-1-0=wontfix;field0-5-0=cf_status_firefox" + aurora_version + ";type0-0-0=equals;value0-0-0=%2B;type0-2-0=notequals;negate1=1;field0-3-0=cf_status_firefox" + aurora_version + ";type0-4-0=notequals;value3-0-0=%2B;value2-0-0=approval-mozilla-aurora%3F;field0-6-0=cf_status_firefox" + aurora_version + ";field3-0-0=cf_tracking_firefox" + beta_version + ";type2-0-0=notsubstring;value0-2-0=fixed;type0-3-0=notequals;value1-0-0=core-security;field0-2-0=cf_status_firefox" + aurora_version + ";field0-4-0=cf_status_firefox" + aurora_version + ";type0-6-0=notequals;field1-0-0=bug_group;field2-1-0=status_whiteboard;type2-1-0=notsubstring;value2-1-0=[no-nag]"
|
||||
|
||||
tracking_central_url = "https://bugzilla.mozilla.org/buglist.cgi?negate1=1;field0-3-0=cf_status_firefox" + central_version + ";value3-1-0=%2B;type1-0-0=equals;type0-1-0=notequals;type0-5-0=notequals;value0-5-0=disabled;value0-4-0=verified;type3-0-0=notequals;field0-1-0=cf_status_firefox" + central_version + ";field0-0-0=cf_tracking_firefox" + central_version + ";type0-4-0=notequals;value3-0-0=%2B;value2-0-0=approval-mozilla-aurora%3F;field2-0-0=flagtypes.name;field3-1-0=cf_tracking_firefox" + aurora_version + ";field0-6-0=cf_status_firefox" + central_version + ";value0-3-0=unaffected;field3-0-0=cf_tracking_firefox" + beta_version + ";type2-0-0=notsubstring;value0-2-0=fixed;value0-6-0=verified%20disabled;value0-1-0=wontfix;type0-3-0=notequals;value1-0-0=core-security;field0-2-0=cf_status_firefox" + central_version + ";type3-1-0=notequals;field0-5-0=cf_status_firefox" + central_version + ";field0-4-0=cf_status_firefox" + central_version + ";type0-6-0=notequals;type0-0-0=equals;value0-0-0=%2B;type0-2-0=notequals;field1-0-0=bug_group;field2-1-0=status_whiteboard;type2-1-0=notsubstring;value2-1-0=[no-nag]"
|
||||
|
||||
tracking_beta_touch_url = "https://bugzilla.mozilla.org/buglist.cgi?negate1=1;field0-3-0=cf_status_firefox" + beta_version + ";type1-0-0=equals;type0-1-0=notequals;type0-5-0=notequals;value0-5-0=disabled;value0-4-0=verified;type3-0-0=greaterthan;field0-1-0=cf_status_firefox" + beta_version + ";field0-0-0=cf_tracking_firefox" + beta_version + ";type0-4-0=notequals;value3-0-0=3;value2-0-0=approval-mozilla-beta%3F;field2-0-0=flagtypes.name;field0-6-0=cf_status_firefox" + beta_version + ";value0-3-0=unaffected;field3-0-0=days_elapsed;type2-0-0=notsubstring;value0-2-0=fixed;value0-6-0=verified%20disabled;value0-1-0=wontfix;type0-3-0=notequals;value1-0-0=core-security;field0-2-0=cf_status_firefox" + beta_version + ";field0-5-0=cf_status_firefox" + beta_version + ";field0-4-0=cf_status_firefox" + beta_version + ";type0-6-0=notequals;type0-0-0=equals;value0-0-0=%2B;type0-2-0=notequals;field1-0-0=bug_group" + no_nag
|
||||
|
||||
tracking_aurora_touch_url = "https://bugzilla.mozilla.org/buglist.cgi?negate1=1;field0-3-0=cf_status_firefox" + aurora_version + ";type4-0-0=greaterthan;type1-0-0=equals;type0-1-0=notequals;type0-5-0=notequals;value0-5-0=disabled;value0-4-0=verified;type3-0-0=notequals;field0-1-0=cf_status_firefox" + aurora_version + ";field0-0-0=cf_tracking_firefox" + aurora_version + ";type0-4-0=notequals;value3-0-0=%2B;field4-0-0=days_elapsed;value2-0-0=approval-mozilla-aurora%3F;field2-0-0=flagtypes.name;field0-6-0=cf_status_firefox" + aurora_version + ";value0-3-0=unaffected;field3-0-0=cf_tracking_firefox" + beta_version + ";type2-0-0=notsubstring;value0-2-0=fixed;value0-6-0=verified%20disabled;value0-1-0=wontfix;type0-3-0=notequals;value1-0-0=core-security;field0-2-0=cf_status_firefox" + aurora_version + ";field0-5-0=cf_status_firefox" + aurora_version + ";value4-0-0=3;field0-4-0=cf_status_firefox" + aurora_version + ";type0-6-0=notequals;type0-0-0=equals;value0-0-0=%2B;type0-2-0=notequals;field1-0-0=bug_group" + no_nag
|
||||
|
||||
tracking_central_touch_url = "https://bugzilla.mozilla.org/buglist.cgi?negate1=1;field0-3-0=cf_status_firefox" + central_version + ";type1-0-0=equals;type2-1-0=notequals;type0-1-0=notequals;type0-5-0=notequals;value0-5-0=disabled;value0-4-0=verified;type3-0-0=greaterthan;field0-1-0=cf_status_firefox" + central_version + ";field0-0-0=cf_tracking_firefox" + central_version + ";type0-4-0=notequals;value3-0-0=3;value2-0-0=%2B;field2-0-0=cf_tracking_firefox" + beta_version + ";field0-6-0=cf_status_firefox" + central_version + ";value0-3-0=unaffected;field3-0-0=days_elapsed;type2-0-0=notequals;value0-2-0=fixed;value0-6-0=verified%" + central_version + "disabled;value0-1-0=wontfix;type0-3-0=notequals;value2-1-0=%2B;value1-0-0=core-security;field0-2-0=cf_status_firefox" + central_version + ";field0-5-0=cf_status_firefox" + central_version + ";field0-4-0=cf_status_firefox" + central_version + ";type0-6-0=notequals;type0-0-0=equals;value0-0-0=%2B;field2-1-0=cf_tracking_firefox" + aurora_version + ";type0-2-0=notequals;field1-0-0=bug_group" + no_nag
|
||||
|
||||
tracking_esr38_url = "https://bugzilla.mozilla.org/buglist.cgi?type0-1-0=nowordssubstr;field0-1-0=cf_status_firefox_esr" + esr_version + ";field0-0-0=cf_tracking_firefox_esr" + esr_version + ";value0-1-0=fixed%20verified%20disabled%20wontfix%20unaffected;type0-0-0=equals;value0-0-0=" + beta_version + "%2B;field0-2-0=status_whiteboard;type0-2-0=notsubstring;value0-2-0=[no-nag]"
|
||||
|
||||
needinfo_beta_url = "https://bugzilla.mozilla.org/buglist.cgi?f1=cf_tracking_firefox" + beta_version + "&v6=fixed%2Cwontfix%2Cunaffected%2Cverified%2Cdisabled&o1=anywords&resolution=---&o6=anywords&f12=flagtypes.name&v12=needinfo%3F&f11=OP&bug_status=UNCONFIRMED,NEW,READY,ASSIGNED,REOPENED&o12=equals&v1=%2B%2C%3F&f6=cf_status_firefox" + beta_version + "&n6=1"
|
||||
|
||||
needinfo_aurora_url = "https://bugzilla.mozilla.org/buglist.cgi?f1=cf_tracking_firefox" + aurora_version + "&v6=fixed%2Cwontfix%2Cunaffected%2Cverified%2Cdisabled&o1=anywords&resolution=---&o6=anywords&f12=flagtypes.name&v12=needinfo%3F&f11=OP&bug_status=UNCONFIRMED,NEW,READY,ASSIGNED,REOPENED&o12=equals&v1=%2B%2C%3F&f6=cf_status_firefox" + aurora_version + "&n6=1"
|
||||
|
||||
needinfo_central_url = "https://bugzilla.mozilla.org/buglist.cgi?f1=cf_tracking_firefox" + central_version + "&v6=fixed%2Cwontfix%2Cunaffected%2Cverified%2Cdisabled&o1=anywords&resolution=---&o6=anywords&f12=flagtypes.name&v12=needinfo%3F&f11=OP&bug_status=UNCONFIRMED,NEW,READY,ASSIGNED,REOPENED&o12=equals&v1=%2B%2C%3F&f6=cf_status_firefox" + central_version + "&n6=1"
|
||||
|
||||
needinfo_esr38_url = "https://bugzilla.mozilla.org/buglist.cgi?f1=cf_tracking_firefox_esr" + esr_version + "&v6=fixed%2Cwontfix%2Cunaffected%2Cverified%2Cdisabled&o1=anywords&resolution=---&o6=anywords&f12=flagtypes.name&v12=needinfo%3F&f11=OP&bug_status=UNCONFIRMED,NEW,READY,ASSIGNED,REOPENED&o12=equals&v1=%2B%2C%3F&f6=cf_status_firefox_esr" + esr_version + "&n6=1"
|
||||
|
||||
# TODO - sort the queries according to a priority flag
|
||||
# TODO - separate queries for sec bugs, for now hide summary
|
||||
# TODO - fix the 'untouched' queries
|
||||
|
||||
# tracked beta/aurora/nightly nobodies: https://bugzilla.mozilla.org/buglist.cgi?j_top=OR&f1=cf_tracking_firefox25&o3=equals&v3=%2B&o1=equals&emailtype1=exact&o2=equals&emailassigned_to1=1&f3=cf_tracking_firefox27&f2=cf_tracking_firefox26&bug_status=UNCONFIRMED&bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED&email1=nobody%40mozilla.org&v1=%2B&v2=%2B
|
||||
# need same but for untouched
|
||||
|
||||
urls = [
|
||||
(5, ["Unlanded Beta " + beta_version + " Bugs", "unlanded_beta", unlanded_beta_url, 0]),
|
||||
(5, ["Unlanded Aurora " + aurora_version + " Bugs", "unlanded_aurora", unlanded_aurora_url, 0]),
|
||||
(5, ["Unlanded ESR38 Bugs", "unlanded_esr38", unlanded_esr38_url, 0]),
|
||||
(5, ["Tracked or Nominated for Tracking with Need-Info? Beta " + beta_version + " Bugs", "needinfo_beta", needinfo_beta_url, 0]),
|
||||
(5, ["Tracked or Nominated for Tracking with Need-Info? Aurora " + aurora_version + " Bugs", "needinfo_aurora", needinfo_aurora_url, 0]),
|
||||
(5, ["Tracked or Nominated for Tracking with Need-Info? Nightly " + central_version + " Bugs", "needinfo_central", needinfo_central_url, 0]),
|
||||
(5, ["Tracked or Nominated for Tracking with Need-Info? ESR38 Bugs", "needinfo_esr38", needinfo_esr38_url, 0]),
|
||||
(0, ["Bugs Tracked for Beta " + beta_version, "tracking_beta", tracking_beta_url, 0]),
|
||||
(0, ["Bugs Tracked for Aurora " + aurora_version, "tracking_aurora", tracking_aurora_url, 0]),
|
||||
(0, ["Bugs Tracked for Nightly " + central_version, "tracking_central", tracking_central_url, 0]),
|
||||
(0, ["Bugs Tracked for ESR38", "tracking_esr38", tracking_esr38_url, 0]),
|
||||
(3, ["Tracked Beta " + beta_version + " Bugs, untouched this week", "untouched_tracking_beta", tracking_beta_touch_url, 0]),
|
||||
(3, ["Tracked Aurora " + aurora_version + " Bugs, untouched this week", "untouched_tracking_aurora", tracking_aurora_touch_url, 0]),
|
||||
(3, ["Tracked Nightly " + central_version + " Bugs, untouched this week", "untouched_tracking_nightly", tracking_central_touch_url, 0])
|
||||
]
|
||||
|
||||
|
||||
def createQuery(title, short_title, url, show_summary):
|
||||
file_name = queries_dir + str(datetime.date.today()) + '_' + short_title
|
||||
if not os.path.exists(queries_dir):
|
||||
os.makedirs(queries_dir)
|
||||
qf = open(file_name, 'w')
|
||||
qf.write("query_name = \'" + title + "\'\n")
|
||||
qf.write("query_url = \'" + url + "\'\n")
|
||||
qf.write("show_summary = \'" + str(show_summary) + "\'\n")
|
||||
return file_name
|
||||
|
||||
|
||||
def createQueriesList(print_all):
|
||||
queries = []
|
||||
weekday = datetime.datetime.today().weekday()
|
||||
for url in urls:
|
||||
if weekday >= 0 and weekday < 5 and url[0] == 5:
|
||||
queries.append(createQuery(title=url[1][0], short_title=url[1][1], url=url[1][2], show_summary=url[1][3]))
|
||||
if weekday == 0 and url[0] == 0:
|
||||
queries.append(createQuery(title=url[1][0], short_title=url[1][1], url=url[1][2], show_summary=url[1][3]))
|
||||
if weekday == 3 and url[0] == 3:
|
||||
queries.append(createQuery(title=url[1][0], short_title=url[1][1], url=url[1][2], show_summary=url[1][3]))
|
||||
print queries
|
||||
return queries
|
||||
|
||||
|
||||
def cleanUp():
|
||||
for file in os.listdir(queries_dir):
|
||||
if file.startswith(str(datetime.date.today())):
|
||||
os.remove(os.path.join(queries_dir, file))
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = ArgumentParser(__doc__)
|
||||
parser.set_defaults(
|
||||
queries_only=False,
|
||||
)
|
||||
parser.add_argument("-q", "--queries-only", dest="queries_only", action="store_true",
|
||||
help="just create and print queries")
|
||||
|
||||
options, args = parser.parse_known_args()
|
||||
queries = createQueriesList(print_all=options.queries_only)
|
||||
if options.queries_only:
|
||||
for url in urls:
|
||||
print url
|
||||
else:
|
||||
command = [
|
||||
scripts_dir + "email_nag.py",
|
||||
"-t", "daily_email",
|
||||
"--no-verification",
|
||||
"-m", config['ldap_username'],
|
||||
"-p", config['ldap_password'],
|
||||
"-b", config['bz_api_key'],
|
||||
"-e", "release-mgmt@mozilla.com"]
|
||||
for query in queries:
|
||||
command.append('-q')
|
||||
command.append(query)
|
||||
subject = datetime.datetime.today().strftime("%A %b %d") + " -- Daily Release Tracking Alert"
|
||||
command.extend(['-s', subject])
|
||||
# send all other args to email_nag script argparser
|
||||
command.extend(args)
|
||||
subprocess.call(command)
|
||||
cleanUp()
|
Загрузка…
Ссылка в новой задаче