summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJames Laska <jlaska@redhat.com>2011-03-24 16:34:35 -0400
committerJames Laska <jlaska@redhat.com>2011-03-24 16:34:35 -0400
commit731cc4fbc20c5709031c99c14c41abbce5589a56 (patch)
tree8568e5381539e5a96838dc5adc478fcfbc11059f
downloadscripts-731cc4fbc20c5709031c99c14c41abbce5589a56.tar.gz
scripts-731cc4fbc20c5709031c99c14c41abbce5589a56.tar.xz
scripts-731cc4fbc20c5709031c99c14c41abbce5589a56.zip
Add python blocker bug -> wiki script
-rwxr-xr-xupdate-blocker-wiki263
1 files changed, 263 insertions, 0 deletions
diff --git a/update-blocker-wiki b/update-blocker-wiki
new file mode 100755
index 0000000..5ff268d
--- /dev/null
+++ b/update-blocker-wiki
@@ -0,0 +1,263 @@
+#!/usr/bin/python
+
+import os
+import sys
+import bugzilla
+import locale
+import optparse
+import getpass
+
+from simplemediawiki import MediaWiki
+
+BUG_STATUS = ['NEW', 'ASSIGNED', 'ON_DEV', 'MODIFIED', 'POST', 'ON_QA', 'FAILS_QA',
+ 'PASSES_QA', 'REOPENED', 'VERIFIED', 'RELEASE_PENDING']
+
+# Blocker query values
+BLOCKER_ACCEPTED = {'status_whiteboard': 'AcceptedBlocker',
+ 'status_whiteboard_type': 'anywords'}
+BLOCKER_PROPOSED = {'status_whiteboard': 'AcceptedBlocker RejectedBlocker',
+ 'status_whiteboard_type': 'nowords'}
+
+# NTH query values
+NTH_ACCEPTED = {'status_whiteboard': 'AcceptedNTH',
+ 'status_whiteboard_type': 'anywords'}
+NTH_PROPOSED = {'status_whiteboard': 'AcceptedNTH RejectedNTH',
+ 'status_whiteboard_type': 'nowords'}
+
+# UTF8 helper
+def to_encoding(ustring):
+ if isinstance(ustring, basestring):
+ if isinstance(ustring, unicode):
+ return ustring.encode(locale.getpreferredencoding(), 'replace')
+ return ustring
+ return u''
+
+# Display list of bugs, organized by components
+def display_bugs_by_component(bugs_by_component):
+
+ buf = ''
+ components = sorted(bugs_by_component.keys())
+ for component in components:
+ buf += '=== %s ===\n' % component
+ # sorted list
+ bugs = sorted(bugs_by_component.get(component,[]))
+ for b in bugs:
+ b = bugs_by_id[b]
+ buf += '* [https://bugzilla.redhat.com/%s %s] (%s) - %s\n' % (b.bug_id, b.bug_id, b.bug_status, b.short_desc)
+ return buf
+
+def parse_args():
+ '''Set up the option parser'''
+ parser = optparse.OptionParser(usage="%prog [options] <action> [options]")
+ parser.add_option('-v', '--verbose', action='store_true',
+ default=False, help='Enable verbosity')
+ parser.add_option('--hotdog', action='store_true',
+ default=False, help='Enable hot dog (default: %default)')
+
+ # Required args
+ parser.add_option('--blocker', action='store',
+ default=None, help='Blocker tracking bug number')
+ parser.add_option('--nth', action='store',
+ default=None, help='Nice-to-Have tracking bug number')
+ parser.add_option('-n', '--name', action='store',
+ default='Current_Release_Blockers',
+ help='Wiki page name to save results')
+ parser.add_option('-u', '--user', action='store',
+ default=None, help='Mediawiki username')
+ parser.add_option('-p', '--passwd', action='store',
+ default=None, help='Mediawiki password')
+
+ (opts, args) = parser.parse_args()
+
+ # sanitize username
+ if opts.user is None and sys.stdin.isatty():
+ opts.user = raw_input('Enter mediawiki username: ')
+ if opts.user is None:
+ parser.error('Must provide a valid username')
+ # sanitize password
+ if opts.passwd is None and sys.stdin.isatty():
+ opts.passwd = getpass.getpass('Enter mediawiki password: ')
+ if opts.passwd is None:
+ parser.error('Must provide a valid password')
+
+ # sanitize blocker number
+ if opts.blocker is None:
+ opts.blocker = raw_input('Enter Blocker bug number: ')
+ if opts.blocker is None:
+ parser.error('Must provide a blocker bug number')
+ # sanitize nth number
+ if opts.nth is None:
+ opts.nth = raw_input('Enter Nice-to-have bug number: ')
+ if opts.nth is None:
+ parser.error('Must provide a nice-to-have bug number')
+
+ return opts
+
+if __name__ == '__main__':
+
+ opts = parse_args()
+
+ # Connect to bugzilla
+ bz = bugzilla.RHBugzilla3(url='https://bugzilla.redhat.com/xmlrpc.cgi')
+
+ # Get a list of accepted blocker bugs
+ if opts.verbose: print 'Querying accepted blocker bugs ...'
+ q = {'bug_status': BUG_STATUS,
+ 'value0-0-0': opts.blocker,
+ 'type0-0-0': 'substring',
+ 'field0-0-0': 'blocked'}
+ q.update(BLOCKER_ACCEPTED)
+ accepted_blockers = bz.query(q)
+
+ # Get a list of proposed blocker bugs
+ if opts.verbose: print 'Querying proposed blocker bugs ...'
+ q = {'bug_status': BUG_STATUS,
+ 'value0-0-0': opts.blocker,
+ 'type0-0-0': 'substring',
+ 'field0-0-0': 'blocked'}
+ q.update(BLOCKER_PROPOSED)
+ proposed_blockers = bz.query(q)
+
+ # Get a list of accepted NTH bugs
+ if opts.verbose: print 'Querying accepted nice-to-have bugs ...'
+ q = {'bug_status': BUG_STATUS,
+ 'value0-0-0': opts.nth,
+ 'type0-0-0': 'substring',
+ 'field0-0-0': 'blocked'}
+ q.update(NTH_ACCEPTED)
+ accepted_nths = bz.query(q)
+
+ # Get a list of proposed NTH bugs
+ if opts.verbose: print 'Querying proposed nice-to-have bugs ...'
+ q = {'bug_status': BUG_STATUS,
+ 'value0-0-0': opts.nth,
+ 'type0-0-0': 'substring',
+ 'field0-0-0': 'blocked'}
+ q.update(NTH_PROPOSED)
+ proposed_nths = bz.query(q)
+
+ # Organize bugs for later reference
+ if opts.verbose: print 'Organizing bugs ...'
+ bugs_by_id = dict()
+
+ # walk accepted blockers
+ accepted_blocker_by_component = dict()
+ for b in accepted_blockers:
+ if not accepted_blocker_by_component.has_key(b.component):
+ accepted_blocker_by_component[b.component] = list()
+ accepted_blocker_by_component[b.component].append(b.bug_id)
+ bugs_by_id[b.bug_id] = b
+
+ # walk proposed blockers
+ proposed_blocker_by_component = dict()
+ for b in proposed_blockers:
+ if not proposed_blocker_by_component.has_key(b.component):
+ proposed_blocker_by_component[b.component] = list()
+ proposed_blocker_by_component[b.component].append(b.bug_id)
+ bugs_by_id[b.bug_id] = b
+
+ # walk accepted nths
+ accepted_nth_by_component = dict()
+ for b in accepted_nths:
+ if not accepted_nth_by_component.has_key(b.component):
+ accepted_nth_by_component[b.component] = list()
+ accepted_nth_by_component[b.component].append(b.bug_id)
+ bugs_by_id[b.bug_id] = b
+
+ # walk proposed nths
+ proposed_nth_by_component = dict()
+ for b in proposed_nths:
+ if not proposed_nth_by_component.has_key(b.component):
+ proposed_nth_by_component[b.component] = list()
+ proposed_nth_by_component[b.component].append(b.bug_id)
+ bugs_by_id[b.bug_id] = b
+
+ # Generate page content
+ page_content = ''
+
+ # Start with the dog
+ if opts.hotdog:
+ page_content += '[[File:Hotdog.gif|right]]'
+ else:
+ page_content += '[[File:F{{FedoraVersionNumber|next}}_anaconda_center.png|right]]'
+
+ # Display approved blockers
+ page_content += '''
+== Approved Blockers ==
+The following list of bugs are approved blockers that must be resolved.
+
+'''
+ # sorted list of approved bugs
+ page_content += display_bugs_by_component(accepted_blocker_by_component)
+
+ # Display proposed blockers
+ page_content += '''
+== Proposed Blockers ==
+The following list of bugs are not yet approved to block the release. For guidance on reviewing the following bugs, refer to [[QA:SOP_blocker_bug_process]].
+
+'''
+ # sorted list of proposed bugs
+ page_content += display_bugs_by_component(proposed_blocker_by_component)
+
+ # Display approved nths
+ page_content += '''
+== Approved NICE-TO-HAVE ==
+The following list of bugs are approved nths that must be resolved.
+
+'''
+ # sorted list of approved bugs
+ page_content += display_bugs_by_component(accepted_nth_by_component)
+
+ # Display proposed nths
+ page_content += '''
+== Proposed NICE-TO-HAVE ==
+The following list of bugs are not yet approved to block the release. For guidance on reviewing the following bugs, refer to [[QA:SOP_nth_bug_process]].
+
+'''
+ # sorted list of proposed bugs
+ page_content += display_bugs_by_component(proposed_nth_by_component)
+
+
+ # Create mediawiki handle
+ if opts.verbose: print 'Connecting to mediawiki ...'
+ wiki = MediaWiki('https://fedoraproject.org/w/api.php')
+
+ # Login to the wiki
+ if not wiki.login(opts.user, opts.passwd):
+ print "Error: invalid username or password"
+ sys.exit(1)
+
+ # Get an edit token
+ q = dict(action='query',
+ prop='info',
+ intoken='edit',
+ titles=opts.name)
+ response = wiki.call(q)
+ pages = [v for k,v in response.get('query', {}).get('pages',{}).items()]
+ edit_token = list()
+ if pages:
+ edit_token = pages[0]
+
+ # Make the wiki edit
+ q = dict(action='edit',
+ token=edit_token.get('edittoken',''),
+ starttimestamp=edit_token.get('starttimestamp',''),
+ summary='scripted update brought to you by python-simplemediawiki',
+ text=to_encoding(u'%s' % page_content),
+ title=opts.name)
+ response = wiki.call(q)
+
+ # {'edit': {'captcha': {'mime': 'text/plain', 'type': 'simple', 'id': '674992674', 'question': '78 - 6'}, 'result': 'Failure'}}
+
+ if response.get('edit',{}).get('result','').lower() == 'failure':
+ # Is a captcha required?
+ captcha = response.get('edit', {}).get('captcha', False)
+ if captcha:
+ q.update(dict(captchaid=captcha.get('id'),
+ captchaword=eval(captcha.get('question'))))
+ response = wiki.call(q)
+ if response.get('result','').lower() == 'failure':
+ print "Failed to update '%s'\n%s" % (q.get('title'), response)
+ sys.exit(1)
+ print "Updated '%s'" % q.get('title')
+ sys.exit(0)