Automatically generate a dash based on "In Progress" bugs
This adds a script to get the "In Progress" bugs for a particular project and builds prints a URL for the user. $ ./gerrit-bug-dash --milestone kilo-rc1 --tag kilo-rc-potential heat Some notes: - it uses launchpadlib to talk to launchpad - The presentation of the dashboard could be improved but this is just a starting point. - The caching could be better. What I wanted was to know was "what do I need to review for rc1" (i.e. what are the bugs that are targeted for kilo-rc1 that are in progress and have reviews up). This scratches my itch, but will happily do some work on it if others want it. Change-Id: I4a97d59631ac9cd344206c6cc48164d6a0d7e57c
This commit is contained in:
parent
a5cb83666c
commit
3bd7dcc189
1
gerrit-bug-dash
Symbolic link
1
gerrit-bug-dash
Symbolic link
@ -0,0 +1 @@
|
||||
gerrit_dash_creator/cmd/bugs.py
|
176
gerrit_dash_creator/cmd/bugs.py
Executable file
176
gerrit_dash_creator/cmd/bugs.py
Executable file
@ -0,0 +1,176 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
|
||||
from launchpadlib import launchpad
|
||||
import six
|
||||
from six.moves import configparser
|
||||
|
||||
from gerrit_dash_creator.cmd import creator
|
||||
|
||||
CACHE_DIR = os.path.expanduser('~/.cache/launchpadlib/')
|
||||
SERVICE_ROOT = 'production'
|
||||
|
||||
|
||||
def print_dash_url(opts, bugs):
|
||||
config = configparser.ConfigParser()
|
||||
config.add_section('dashboard')
|
||||
title = ','.join(opts.projects)
|
||||
if opts.milestone:
|
||||
title += ' milestone:%s' % opts.milestone
|
||||
if opts.tag:
|
||||
title += ' AND'
|
||||
if opts.tag:
|
||||
title += ' tag:%s' % opts.tag
|
||||
|
||||
config.set('dashboard', 'title', title)
|
||||
config.set('dashboard', 'description', 'Bug Fix Inbox')
|
||||
|
||||
proj_q = ['project:openstack/%s' % proj for proj in opts.projects]
|
||||
config.set('dashboard', 'foreach',
|
||||
'(%s) status:open ' % ' OR '.join(proj_q))
|
||||
|
||||
for label in bugs:
|
||||
for prio in bugs[label]:
|
||||
if len(bugs[label][prio]) == 0:
|
||||
continue
|
||||
sect = 'section "%s Importance %s"' % (label, prio)
|
||||
if prio == 'None':
|
||||
sect = 'section "%s"' % label
|
||||
config.add_section(sect)
|
||||
config.set(sect, 'query',
|
||||
' OR '.join(['change:%s' % bug
|
||||
for bug in bugs[label][prio]]))
|
||||
|
||||
print(creator.generate_dashboard_url(config))
|
||||
|
||||
|
||||
def pretty_milestone(milestone_url):
|
||||
if milestone_url is None:
|
||||
return 'Unassigned'
|
||||
# https://api.launchpad.net/1.0/heat/+milestone/next:
|
||||
return str(milestone_url).split('/')[-1]
|
||||
|
||||
|
||||
def review_id_from_bug(bug, project_name):
|
||||
reviews = set()
|
||||
reviews_ignored = set()
|
||||
for msg in bug.bug.messages:
|
||||
try:
|
||||
lines = six.text_type(msg.content).split('\n')
|
||||
except UnicodeEncodeError:
|
||||
print('non-ascii in bug %s' % bug.web_link)
|
||||
continue
|
||||
|
||||
proposed = 'ix proposed to %s' % project_name
|
||||
merged = 'ix merged to %s' % project_name
|
||||
abandoned = 'Change abandoned on %s' % project_name
|
||||
if proposed in msg.subject:
|
||||
for line in lines:
|
||||
if 'Review: ' in line:
|
||||
reviews.add(line.split('/')[-1])
|
||||
if merged in msg.subject or abandoned in msg.subject:
|
||||
for line in lines:
|
||||
if 'Review' in line:
|
||||
reviews_ignored.add(line.split('/')[-1])
|
||||
live_reviews = (reviews - reviews_ignored)
|
||||
if len(live_reviews) == 0:
|
||||
print('bug %s has no reviews set to Triaged state' % bug.web_link)
|
||||
return live_reviews
|
||||
|
||||
|
||||
def get_options():
|
||||
"""Parse command line arguments and options."""
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Create a Gerrit dashboard URL from launchpad '
|
||||
'"In Progress bugs')
|
||||
parser.add_argument('projects', nargs='+',
|
||||
metavar='projects',
|
||||
help='Launchpad Projects')
|
||||
parser.add_argument('--milestone', default=None,
|
||||
help='Project Milestone')
|
||||
parser.add_argument('--tag', default=None,
|
||||
help='Project Tag')
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def process_project(lp, opts, project_name, bugs):
|
||||
project = lp.projects[project_name]
|
||||
|
||||
if opts.tag is None and opts.milestone is not None:
|
||||
from_milestone = project.getMilestone(name=opts.milestone)
|
||||
if not from_milestone:
|
||||
print('Origin milestone %s does not exist' %
|
||||
opts.milestone)
|
||||
sys.exit(1)
|
||||
|
||||
review_bugtasks = from_milestone.searchTasks(status=['In Progress'])
|
||||
else:
|
||||
review_bugtasks = project.searchTasks(status=['In Progress'])
|
||||
|
||||
for bug in review_bugtasks:
|
||||
importance = bug.importance
|
||||
milestone = pretty_milestone(bug.milestone)
|
||||
tags = bug.bug.tags
|
||||
|
||||
label = None
|
||||
if opts.tag is not None:
|
||||
if opts.tag in tags:
|
||||
label = 'Tag:%s' % opts.tag
|
||||
|
||||
if opts.milestone is not None:
|
||||
if milestone == opts.milestone:
|
||||
label = 'Milestone:%s' % milestone
|
||||
|
||||
if opts.tag is None and opts.milestone is None:
|
||||
# just place by milestone
|
||||
label = 'Milestone:%s' % milestone
|
||||
importance = 'None'
|
||||
|
||||
if label is None:
|
||||
continue
|
||||
|
||||
if label not in bugs:
|
||||
bugs[label] = {}
|
||||
|
||||
if importance not in bugs[label]:
|
||||
bugs[label][importance] = []
|
||||
|
||||
for rev_no in review_id_from_bug(bug, project_name):
|
||||
bugs[label][importance].append(rev_no)
|
||||
print('[%s] %s -> %s' % (importance,
|
||||
bug.web_link, rev_no))
|
||||
|
||||
|
||||
def main():
|
||||
"""Entrypoint."""
|
||||
|
||||
opts = get_options()
|
||||
lpad = launchpad.Launchpad.login_anonymously(sys.argv[0],
|
||||
SERVICE_ROOT,
|
||||
CACHE_DIR)
|
||||
bugs = {}
|
||||
for proj in opts.projects:
|
||||
process_project(lpad, opts, proj, bugs)
|
||||
|
||||
print('')
|
||||
print_dash_url(opts, bugs)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
@ -6,3 +6,4 @@ argparse
|
||||
jinja2
|
||||
pbr>=0.6,!=0.7,<1.0
|
||||
six>=1.7.0
|
||||
launchpadlib
|
||||
|
Loading…
Reference in New Issue
Block a user