Merge "Remove run-mirror from jeepyb"
diff --git a/jeepyb/cmd/notify_impact.py b/jeepyb/cmd/notify_impact.py
index af1ad8a..2214ffb 100644
--- a/jeepyb/cmd/notify_impact.py
+++ b/jeepyb/cmd/notify_impact.py
@@ -17,6 +17,19 @@
# patchsets for strings like "bug FOO" and updates corresponding Launchpad
# bugs status.
+# You want to test this? I use a command line a bit like this:
+# python notify_impact.py --change 55607 \
+# --change-url https://review.openstack.org/55607 --project nova/ \
+# --branch master --commit c262de4417d48be599c3a7496ef94de5c84b188c \
+# --impact DocImpact --dest-address none@localhost --dryrun \
+# --ignore-duplicates \
+# change-merged
+#
+# But you'll need a git repository at /home/gerrit2/review_site/git/nova.git
+# for that to work
+
+from __future__ import print_function
+
import argparse
import os
import re
@@ -26,6 +39,7 @@
from email.mime import text
from launchpadlib import launchpad
from launchpadlib import uris
+import yaml
BASE_DIR = '/home/gerrit2/review_site'
EMAIL_TEMPLATE = """
@@ -45,7 +59,39 @@
'~/.launchpadlib/creds'))
-def create_bug(git_log, args, lp_project):
+class BugActionsReal(object):
+ """Things we do to bugs."""
+
+ def __init__(self, lpconn):
+ self.lpconn = lpconn
+
+ def create(self, project, bug_title, bug_descr, args):
+ buginfo = self.lpconn.bugs.createBug(
+ target=project, title=bug_title,
+ description=bug_descr, tags=args.project.split('/')[1])
+ buglink = buginfo.web_link
+ return buginfo, buglink
+
+ def subscribe(self, buginfo, subscriber):
+ user = self.lpconn.people[subscriber]
+ if user:
+ buginfo.subscribe(person=user)
+
+
+class BugActionsDryRun(object):
+ def __init__(self, lpconn):
+ self.lpconn = lpconn
+
+ def create(self, project, bug_title, bug_descr, args):
+ print('I would have created a bug, but I am in dry run mode')
+ return None, None
+
+ def subscribe(self, buginfo, subscriber):
+ print('I would have added %s as a subscriber to the bug, '
+ 'but I am in dry run mode' % subscriber)
+
+
+def create_bug(git_log, args, lp_project, subscribers):
"""Create a bug for a change.
Create a launchpad bug in lp_project, titled with the first line of
@@ -60,23 +106,44 @@
credentials_file=GERRIT_CREDENTIALS,
version='devel')
+ if args.dryrun:
+ actions = BugActionsDryRun(lpconn)
+ else:
+ actions = BugActionsReal(lpconn)
+
lines_in_log = git_log.split("\n")
bug_title = lines_in_log[4]
bug_descr = args.change_url + '\n' + git_log
project = lpconn.projects[lp_project]
+
# check for existing bugs by searching for the title, to avoid
# creating multiple bugs per review
+ buglink = None
+ author_class = None
potential_dupes = project.searchTasks(search_text=bug_title)
- if len(potential_dupes) == 0:
- buginfo = lpconn.bugs.createBug(
- target=project, title=bug_title,
- description=bug_descr, tags=args.project.split('/')[1])
- buglink = buginfo.web_link
+
+ if len(potential_dupes) == 0 or args.ignore_duplicates:
+ buginfo, buglink = actions.create(project, bug_title, bug_descr, args)
+
+ # If the author of the merging patch matches our configured
+ # subscriber lists, then subscribe the configured victims.
+ for email_address in subscribers.get('author_map', {}):
+ email_re = re.compile('^Author:.*%s.*' % email_address)
+ for line in bug_descr.split('\n'):
+ m = email_re.match(line)
+ if m:
+ author_class = subscribers['author_map'][email_address]
+
+ if author_class:
+ subscribers = \
+ subscribers.get('subscriber_map', {}).get(author_class, [])
+ for subscriber in subscribers:
+ actions.subscribe(buginfo, subscriber)
return buglink
-def process_impact(git_log, args):
+def process_impact(git_log, args, subscribers):
"""Process DocImpact flag.
If the 'DocImpact' flag is present for a change that is merged,
@@ -87,7 +154,7 @@
"""
if args.impact.lower() == 'docimpact':
if args.hook == "change-merged":
- create_bug(git_log, args, 'openstack-manuals')
+ create_bug(git_log, args, 'openstack-manuals', subscribers)
return
email_content = EMAIL_TEMPLATE % (args.impact,
@@ -121,29 +188,69 @@
def main():
parser = argparse.ArgumentParser()
parser.add_argument('hook')
- #common
+
+ # common
parser.add_argument('--change', default=None)
parser.add_argument('--change-url', default=None)
parser.add_argument('--project', default=None)
parser.add_argument('--branch', default=None)
parser.add_argument('--commit', default=None)
- #change-merged
+
+ # change-merged
parser.add_argument('--submitter', default=None)
- #patchset-created
+
+ # patchset-created
parser.add_argument('--uploader', default=None)
parser.add_argument('--patchset', default=None)
+
# Not passed by gerrit:
parser.add_argument('--impact', default=None)
parser.add_argument('--dest-address', default=None)
+ # Automatic subscribers
+ parser.add_argument('--auto-subscribers', type=argparse.FileType('r'),
+ default=None)
+
+ # Don't actually create the bug
+ parser.add_argument('--dryrun', dest='dryrun', action='store_true')
+ parser.add_argument('--no-dryrun', dest='dryrun', action='store_false')
+ parser.set_defaults(dryrun=False)
+
+ # Ignore duplicates, useful for testing
+ parser.add_argument('--ignore-duplicates', dest='ignore_duplicates',
+ action='store_true')
+ parser.add_argument('--no-ignore-duplicates', dest='ignore_duplicates',
+ action='store_false')
+ parser.set_defaults(ignore_duplicates=False)
+
args = parser.parse_args()
+ # NOTE(mikal): the basic idea here is to let people watch
+ # docimpact bugs filed by people of interest. For example
+ # my team's tech writer wants to be subscribed to all the
+ # docimpact bugs we create. The config for that would be
+ # something like:
+ #
+ # author_map:
+ # mikal@stillhq.com: rcbau
+ # grumpy@dwarves.com: rcbau
+ #
+ # subscriber_map:
+ # rcbau: ['mikalstill', 'grumpypants']
+ #
+ # Where the entries in the author map are email addresses
+ # to match in author lines, and the subscriber map is a
+ # list of launchpad user ids.
+ subscribers = {}
+ if args.auto_subscribers:
+ subscribers = yaml.load(args.auto_subscribers.read())
+
# Get git log
git_log = extract_git_log(args)
# Process impacts found in git log
if impacted(git_log, args.impact):
- process_impact(git_log, args)
+ process_impact(git_log, args, subscribers)
if __name__ == "__main__":
main()
diff --git a/jeepyb/config/subscribers-sample b/jeepyb/config/subscribers-sample
new file mode 100644
index 0000000..c8cc608
--- /dev/null
+++ b/jeepyb/config/subscribers-sample
@@ -0,0 +1,6 @@
+author_map:
+ mikal@stillhq.com: rcbau
+ grumpy@dwarves.com: rcbau
+
+subscriber_map:
+ rcbau: ['mikalstill']
\ No newline at end of file
diff --git a/jeepyb/projects.py b/jeepyb/projects.py
index c1de885..19f178b 100644
--- a/jeepyb/projects.py
+++ b/jeepyb/projects.py
@@ -145,6 +145,11 @@
'openstack/oslo-incubator': 'oslo',
'openstack/tripleo-incubator': 'tripleo',
'openstack/django_openstack_auth': 'django-openstack-auth',
+ 'openstack/savanna': 'savanna',
+ 'openstack/python-savannaclient': 'savanna',
+ 'openstack/savanna-dashboard': 'savanna',
+ 'openstack/savanna-image-elements': 'savanna',
+ 'openstack/savanna-extra': 'savanna',
'openstack-infra/askbot-theme': 'openstack-ci',
'openstack-infra/config': 'openstack-ci',
'openstack-infra/devstack-gate': 'openstack-ci',
@@ -182,12 +187,11 @@
'stackforge/puppet-quantum': 'puppet-neutron',
'stackforge/tripleo-heat-templates': 'tripleo',
'stackforge/tripleo-image-elements': 'tripleo',
- 'stackforge/savanna': 'savanna',
- 'stackforge/savanna-dashboard': 'savanna',
- 'stackforge/savanna-extra': 'savanna',
- 'stackforge/savanna-image-elements': 'savanna',
- 'stackforge/python-savannaclient': 'savanna',
- 'stackforge/puppet-savanna': 'savanna'
+ 'stackforge/puppet-savanna': 'savanna',
+ 'stackforge/fuel-web': 'fuel',
+ 'stackforge/fuel-astute': 'fuel',
+ 'stackforge/fuel-ostf': 'fuel',
+ 'stackforge/fuel-main': 'fuel'
}
return project_map.get(project_full_name,
u.short_project_name(project_full_name))