[Libreoffice-commits] dev-tools.git: scripts/dumpbz
Bjoern Michaelsen
bjoern.michaelsen at canonical.com
Thu Mar 27 02:44:23 PDT 2014
scripts/dumpbz | 367 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 367 insertions(+)
New commits:
commit ec9e3cbf3f0d5a07c29f293c1805a8b5ecd9fa18
Author: Bjoern Michaelsen <bjoern.michaelsen at canonical.com>
Date: Thu Mar 27 10:41:18 2014 +0100
add bugzilla dumper
Change-Id: I37eeda5284160bafe75615be4fe15504a33a7392
Reviewed-on: https://gerrit.libreoffice.org/8769
Reviewed-by: Björn Michaelsen <bjoern.michaelsen at canonical.com>
Tested-by: Björn Michaelsen <bjoern.michaelsen at canonical.com>
diff --git a/scripts/dumpbz b/scripts/dumpbz
new file mode 100755
index 0000000..c83e234
--- /dev/null
+++ b/scripts/dumpbz
@@ -0,0 +1,367 @@
+#!/usr/bin/env python3
+#
+# Version: MPL 1.1 / GPLv3+ / LGPLv3+
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License or as specified alternatively below. You may obtain a copy of
+# the License at http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# Major Contributor(s):
+# [ Copyright (C) 2014 Bjoern Michaelsen <bjoern.michaelsen at canonical.com>]
+#
+# All Rights Reserved.
+#
+# For minor contributions see the git repository.
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 3 or later (the "GPLv3+"), or
+# the GNU Lesser General Public License Version 3 or later (the "LGPLv3+"),
+# in which case the provisions of the GPLv3+ or the LGPLv3+ are applicable
+# instead of those above.
+
+import configparser
+import optparse
+import os
+import os.path
+import shutil
+import subprocess
+import sys
+import time
+import tempfile
+import unittest
+import urllib.request
+
+class DumpBzConfig(configparser.ConfigParser):
+ def set_defaults(self):
+ self['DumpBz'] = {
+ 'BatchSize' : 1000,
+ 'UpdateAge' : '30h',
+ 'IdleTime' : 1000 }
+
+class DumpBzState(configparser.ConfigParser):
+ def set_defaults(self):
+ self['DumpBz'] = {
+ 'LastBugSynced' : 0,
+ 'UnresolvedSyned' : False }
+
+class WorkingBranchCopy:
+ def __init__(self, gitrepo, branch):
+ (self.gitrepo, self.branch) = (gitrepo, branch)
+ def __enter__(self):
+ self.workdir = tempfile.mkdtemp()
+ subprocess.check_output(['git', 'clone', '-s', '-b', self.branch, self.gitrepo, self.workdir], stderr=subprocess.DEVNULL)
+ return self
+ def __exit__(self, t, v, trace):
+ shutil.rmtree(self.workdir)
+ def commit_and_push(self, message):
+ subprocess.check_output(['git', '-C', self.workdir, 'add', '-A'], stderr=None)
+ subprocess.check_output(['git', '-C', self.workdir, 'commit', '--allow-empty', '-m', message], stderr=subprocess.DEVNULL)
+ subprocess.check_output(['git', '-C', self.workdir, 'push', 'origin', self.branch + ':' + self.branch], stderr=subprocess.DEVNULL)
+
+class LocalGitStorage:
+ def __init__(self, basedir):
+ self.basedir = basedir
+ def __write_ini(self, branch, filename, ini):
+ with WorkingBranchCopy(self.basedir, branch) as workcopy:
+ with open(os.path.join(workcopy.workdir, filename), 'w') as inifile:
+ ini.write(inifile)
+ workcopy.commit_and_push('updated ' + filename)
+ def __read_ini(self, branch, filename, ini):
+ with WorkingBranchCopy(self.basedir, branch) as workcopy:
+ ini.read(os.path.join(workcopy.workdir, filename))
+ def write_config(self, config):
+ self.__write_ini('config', 'config', config)
+ def read_config(self, config):
+ return self.__read_ini('config', 'config', config)
+ def write_state(self, state):
+ self.__write_ini('state', 'state', state)
+ def read_state(self, state):
+ return self.__read_ini('state', 'state', state)
+ def create_pocket_dirs(self):
+ for pocket in range(0,1000):
+ asstring = "%03d" % pocket
+ dirname = os.path.join(asstring[-1], asstring)
+ dirpath = os.path.join(self.basedir, dirname)
+ if not os.path.isdir(dirpath):
+ os.makedirs(dirpath)
+ def setup(self):
+ subprocess.check_output(['git', 'init', self.basedir], stderr=subprocess.DEVNULL)
+ subprocess.check_output(['git', '-C', self.basedir, 'commit', '--allow-empty', '-m', 'init'], stderr=subprocess.DEVNULL)
+ subprocess.check_output(['git', '-C', self.basedir, 'checkout', '-b', 'config'], stderr=subprocess.DEVNULL)
+ subprocess.check_output(['git', '-C', self.basedir, 'checkout', '-b', 'state'], stderr=subprocess.DEVNULL)
+ subprocess.check_output(['git', '-C', self.basedir, 'checkout', '-b', 'log'], stderr=subprocess.DEVNULL)
+ subprocess.check_output(['git', '-C', self.basedir, 'checkout', 'master'], stderr=subprocess.DEVNULL)
+ self.create_pocket_dirs()
+ config = DumpBzConfig()
+ config.set_defaults()
+ self.write_config(config)
+ state = DumpBzState()
+ state.set_defaults()
+ self.write_state(state)
+ def reset(self):
+ subprocess.check_output(['git', '-C', self.basedir, 'clean', '-dfx'], stderr=subprocess.DEVNULL)
+ subprocess.check_output(['git', '-C', self.basedir, 'reset', '--hard', 'HEAD'], stderr=subprocess.DEVNULL)
+ subprocess.check_output(['git', '-C', self.basedir, 'checkout', 'master'], stderr=subprocess.DEVNULL)
+ self.create_pocket_dirs()
+ def commit(self, message):
+ subprocess.check_output(['git', '-C', self.basedir, 'add', '-A'], stderr=subprocess.DEVNULL)
+ subprocess.check_output(['git', '-C', self.basedir, 'commit', '-m', message], stderr=subprocess.DEVNULL)
+ def get_bugfilename(self, bug_id):
+ bug_as_str = "%03d" % int(bug_id)
+ return os.path.join(self.basedir, bug_as_str[-1], bug_as_str[-3:], bug_id)
+
+class BugzillaWrapper:
+ def get_initial_bug_id(self):
+ pass
+ def get_batch_of_bug_ids(self, offset, batchsize, unresolved):
+ pass
+ def get_changed_bug_ids(self, since):
+ pass
+ def get_bug_xml(self, bug_id):
+ pass
+
+class FdoBugzillaWrapper:
+ FDOBZ='https://bugs.freedesktop.org/'
+ BATCHQUERY = 'buglist.cgi?query_format=advanced&ctype=csv&f1=bug_id&columnlist=bug_id&o1=greaterthan&v1=%d&product=LibreOffice&limit=%d&order=bug_id'
+ CHANGEDQUERY = 'buglist.cgi?query_format=advanced&ctype=csv&f1=bug_id&columnlist=bug_id&chfieldto=Now&chfieldfrom=%s&product=LibreOffice'
+ UNRESOLVEDONLY='&resolution=---'
+ BUGXMLQUERY = 'show_bug.cgi?ctype=xml&id=%d'
+ def __batch_buglist_url(self, offset, limit, unresolved_only):
+ url = FdoBugzillaWrapper.FDOBZ + FdoBugzillaWrapper.BATCHQUERY % (offset, limit)
+ if unresolved_only:
+ url += FdoBugzillaWrapper.UNRESOLVEDONLY
+ return url
+ def __changed_buglist_url(self, since):
+ url = FdoBugzillaWrapper.FDOBZ + FdoBugzillaWrapper.CHANGEDQUERY % (since)
+ return url
+ def __query_bug_list(self, query):
+ opener = urllib.request.urlopen(query)
+ bug_ids = str(opener.read(), encoding='utf8')
+ bug_ids = bug_ids.split('\n')[1:]
+ return bug_ids
+ def get_batch_of_bug_ids(self, offset, batchsize, unresolved):
+ return self.__query_bug_list(self.__batch_buglist_url(int(offset), int(batchsize), unresolved))
+ def get_initial_bug_id(self):
+ return '0'
+ def get_changed_bug_ids(self, since):
+ return self.__query_bug_list(self.__changed_buglist_url(since))
+ def get_bug_xml(self, bug_id):
+ opener = urllib.request.urlopen(FdoBugzillaWrapper.FDOBZ + FdoBugzillaWrapper.BUGXMLQUERY % int(bug_id))
+ return str(opener.read(), encoding='utf8')
+
+class SyncWorker:
+ def __init__(self, storage, bzwrapper):
+ (self.storage, self.bzwrapper, self.log) = (storage, bzwrapper, None)
+ self.config = DumpBzConfig()
+ self.storage.read_config(self.config)
+ def __sync_bug(self, bug_id):
+ try:
+ with open(self.storage.get_bugfilename(bug_id), 'w') as bugfile:
+ bugfile.write(self.bzwrapper.get_bug_xml(bug_id))
+ time.sleep(float(self.config['DumpBz']['IdleTime'])/1000)
+ except Exception as e:
+ self.log.write("syncing bug %s FAILED: %s\n" % (bug_id, str(e)))
+ else:
+ self.log.write("syncing bug %s succeeded.\n" % bug_id)
+ def __update_changed(self):
+ try:
+ since = self.config['DumpBz']['UpdateAge']
+ bug_ids = self.bzwrapper.get_changed_bug_ids("-1d")
+ self.log.write('Updating changed bugs since %s: %s\n' % (since, repr(bug_ids)))
+ for bug_id in self.bzwrapper.get_changed_bug_ids(self.config['DumpBz']['UpdateAge']):
+ self.__sync_bug(bug_id)
+ except Exception as e:
+ self.log.write("updating changed bugs FAILED: %s\n" % str(e))
+ def __update_batch(self, state):
+ try:
+ last_bug_id = state['DumpBz']['LastBugSynced']
+ if last_bug_id == '0':
+ last_bug_id = self.bzwrapper.get_initial_bug_id()
+ batchsize = self.config['DumpBz']['BatchSize']
+ unresolved = not state.getboolean('DumpBz', 'UnresolvedSyned')
+ unresolved_logaddition = ''
+ if unresolved:
+ unresolved_logaddition = 'unresolved '
+ bug_ids = self.bzwrapper.get_batch_of_bug_ids(last_bug_id, batchsize, unresolved)
+ self.log.write('Looked for %d %sbugs starting at %d, got: %s\n' % (int(batchsize), unresolved_logaddition, int(last_bug_id), repr(bug_ids)))
+ for bug_id in bug_ids:
+ self.__sync_bug(bug_id)
+ state['DumpBz']['LastBugSynced'] = bug_id
+ if len(bug_ids) < int(batchsize):
+ self.log.write('Last batch, starting next cycle.\n')
+ state['DumpBz']['UnresolvedSyned'] = True
+ state['DumpBz']['LastBugSynced'] = 0
+ except Exception as e:
+ self.log.write("updating old batch FAILED: %s\n" % str(e))
+ return state
+ def __do_update_step(self, state):
+ self.storage.reset()
+ self.__update_changed()
+ state = self.__update_batch(state)
+ self.storage.commit('update')
+ return state
+ def run(self):
+ with WorkingBranchCopy(self.storage.basedir, 'log') as logcopy:
+ try:
+ with open(os.path.join(logcopy.workdir, 'log'),'a') as self.log:
+ state = DumpBzState()
+ self.storage.read_state(state)
+ self.storage.write_state(self.__do_update_step(state))
+ finally:
+ logcopy.commit_and_push('update log')
+
+# only test classes beyond this point
+
+class TestWorkingBranchCopy(unittest.TestCase):
+ def setUp(self):
+ self.testdir = tempfile.mkdtemp()
+ subprocess.check_output(['git', 'init', self.testdir], stderr=subprocess.DEVNULL)
+ subprocess.check_output(['git', '-C', self.testdir, 'commit', '--allow-empty', '-m', 'init'], stderr=subprocess.DEVNULL)
+ subprocess.check_output(['git', '-C', self.testdir, 'checkout', '-b', 'somebranch'], stderr=subprocess.DEVNULL)
+ subprocess.check_output(['git', '-C', self.testdir, 'checkout', '-b', 'deadbranch'], stderr=subprocess.DEVNULL)
+ def tearDown(self):
+ shutil.rmtree(self.testdir)
+ def create_new_file(self, branch):
+ with WorkingBranchCopy(self.testdir, branch) as workcopy:
+ with open(os.path.join(workcopy.workdir, branch + '_testfile'), 'w') as testfile:
+ testfile.write(branch + '_filecontent')
+ workcopy.commit_and_push(branch + '_commitmessage')
+ last_commit = str(subprocess.check_output(['git', '-C', self.testdir, 'show', branch]))
+ self.assertRegex(last_commit, branch + '_filecontent')
+ self.assertRegex(last_commit, branch + '_commitmessage')
+ self.assertRegex(last_commit, branch + '_testfile')
+ def test_master(self):
+ self.create_new_file('master')
+ def test_branch(self):
+ self.create_new_file('somebranch')
+
+class TestDumpBzConfig(unittest.TestCase):
+ def setUp(self):
+ self.testdir = tempfile.mkdtemp()
+ self.testfile = os.path.join(self.testdir, 'config')
+ def tearDown(self):
+ shutil.rmtree(self.testdir)
+ def test_default_roundtrip(self):
+ config = DumpBzConfig()
+ config.set_defaults()
+ self.assertIn('DumpBz', config)
+ self.assertIn('BatchSize', config['DumpBz'])
+ self.assertIn('UpdateAge', config['DumpBz'])
+ self.assertIn('IdleTime', config['DumpBz'])
+ with open(self.testfile, 'w') as configfile:
+ config.write(configfile)
+ readconfig = DumpBzConfig()
+ readconfig.read(self.testfile)
+ self.assertIn('DumpBz', readconfig)
+ self.assertIn('BatchSize', readconfig['DumpBz'])
+ self.assertIn('UpdateAge', readconfig['DumpBz'])
+ self.assertIn('IdleTime', readconfig['DumpBz'])
+
+class TestLocalGitStorage(unittest.TestCase):
+ def setUp(self):
+ self.testdir = tempfile.mkdtemp()
+ self.storage = LocalGitStorage(self.testdir)
+ def tearDown(self):
+ shutil.rmtree(self.testdir)
+ def test_setup(self):
+ self.storage.setup()
+ config = DumpBzConfig()
+ self.storage.read_config(config)
+ self.assertIn('DumpBz', config)
+ self.assertIn('BatchSize', config['DumpBz'])
+ self.assertIn('IdleTime', config['DumpBz'])
+ state = DumpBzState()
+ self.storage.read_state(config)
+ self.assertIn('DumpBz', config)
+ self.assertIn('LastBugSynced', config['DumpBz'])
+ self.assertIn('UnresolvedSyned', config['DumpBz'])
+ def test_update(self):
+ self.storage.setup()
+ self.storage.reset()
+ with open(self.storage.get_bugfilename('55555'), 'w') as testfile:
+ testfile.write('test_update_filecontent')
+ self.storage.commit('test_update_commitmessage')
+ last_commit = str(subprocess.check_output(['git', '-C', self.testdir, 'show', 'master']))
+ self.assertRegex(last_commit, 'test_update_filecontent')
+ self.assertRegex(last_commit, 'test_update_commitmessage')
+ self.assertRegex(last_commit, '55555')
+
+class BugzillaWrapperMock(BugzillaWrapper):
+ def get_initial_bug_id(self):
+ return '9999'
+ def get_batch_of_bug_ids(self, offset, batchsize, unresolved):
+ return [str(bug_id) for bug_id in range(int(offset), int(offset)-int(batchsize), -1)]
+ def get_changed_bug_ids(self, since):
+ return ['1', '2', '3']
+ def get_bug_xml(self, bug_id):
+ return 'bug content %s' % bug_id
+
+class TestSyncWorker(unittest.TestCase):
+ def setUp(self):
+ self.testdir = tempfile.mkdtemp()
+ self.storage = LocalGitStorage(self.testdir)
+ self.storage.setup()
+ config = DumpBzConfig()
+ self.storage.read_config(config)
+ config['DumpBz']['BatchSize'] = '10'
+ self.storage.write_config(config)
+ self.bzwrapper = BugzillaWrapperMock()
+ self.worker = SyncWorker(self.storage, self.bzwrapper)
+ def tearDown(self):
+ shutil.rmtree(self.testdir)
+ def verify_bug_id(self, commit, bug_id):
+ self.assertRegex(commit, 'bug content %s' % bug_id)
+ self.assertRegex(commit, self.storage.get_bugfilename(bug_id).replace(self.storage.basedir, '', 1))
+ def test_run(self):
+ self.worker.run()
+ commit = str(subprocess.check_output(['git', '-C', self.testdir, 'show', 'master']))
+ for bug_id in self.bzwrapper.get_changed_bug_ids(None):
+ self.verify_bug_id(commit, bug_id)
+ for bug_id in self.bzwrapper.get_batch_of_bug_ids('9999','10',True):
+ self.verify_bug_id(commit, bug_id)
+
+class TestFdoBugzillaWrapper(unittest.TestCase):
+ def setUp(self):
+ self.fdowrapper = FdoBugzillaWrapper()
+ def __check_bug_list(self, bug_ids):
+ for bug_id in bug_ids:
+ self.assertGreater(int(bug_id), 0)
+ def test_get_batch_of_bug_ids(self):
+ bug_ids = self.fdowrapper.get_batch_of_bug_ids(0, 15, False)
+ self.assertEqual(len(bug_ids), 15)
+ self.__check_bug_list(bug_ids)
+ def test_get_initial_bug_id(self):
+ bug_id = self.fdowrapper.get_initial_bug_id()
+ def test_get_changed_bug_ids(self):
+ bug_ids = self.fdowrapper.get_changed_bug_ids('1d')
+ self.__check_bug_list(bug_ids)
+ def test_get_bug_xml(self):
+ bug_xml = self.fdowrapper.get_bug_xml(10000)
+ self.assertRegex(bug_xml, 'bugzilla version')
+
+if __name__ == '__main__':
+ parser = optparse.OptionParser()
+ parser.add_option('--selftest', action='store_true', dest='selftest', default=False)
+ parser.add_option('--storage', action='store', dest='storage')
+ parser.add_option('--setup', action='store_true', dest='setup', default=False)
+ (options, args) = parser.parse_args()
+ if options.selftest:
+ unittest.main(argv=[sys.argv[0]])
+ else:
+ if not options.storage:
+ sys.exit(1)
+ bzwrapper = FdoBugzillaWrapper()
+ storage = LocalGitStorage(options.storage)
+ if options.setup:
+ storage.setup()
+ sys.exit(0)
+ worker = SyncWorker(storage, bzwrapper)
+ worker.run()
+
+# vim:set shiftwidth=4 softtabstop=4 expandtab: */
More information about the Libreoffice-commits
mailing list