def test_get_bugs_list(self): """previous versions of python-debianbts accepted malformed key-value lists.""" l = bts.get_bugs('submitter', '*****@*****.**', 'severity', 'normal') l2 = bts.get_bugs(['submitter', '*****@*****.**', 'severity', 'normal']) self.assertFalse(len(l) == 0) l.sort() l2.sort() self.assertEqual(l, l2)
def test_get_bugs_empty(caplog): """get_bugs should return empty list if no matching bugs where found.""" bugs = bts.get_bugs(package="thisisatest") assert bugs == [] bugs = bts.get_bugs("package", "thisisatest") assert bugs == [] assert "deprecated" in caplog.text
def test_get_bugs_list(self): """previous versions of python-debianbts accepted malformed key-value lists.""" l = bts.get_bugs('submitter', '*****@*****.**', 'severity', 'normal') l2 = bts.get_bugs( ['submitter', '*****@*****.**', 'severity', 'normal']) self.assertFalse(len(l) == 0) l.sort() l2.sort() self.assertEqual(l, l2)
def bugs_affecting(self, package): if not package in self._bugs_affecting_package: self._misses += 1 signal(SIGALRM, alarm_handler) alarm(120) bugs = debianbts.get_bugs('affects', package, 'bugs', self.all_piuparts_bugs(), 'archive', 'both') bugs += debianbts.get_bugs('affects', 'src:' + package, 'bugs', self.all_piuparts_bugs(), 'archive', 'both') alarm(0) self._bugs_affecting_package[package] = sorted(set(bugs), reverse=True) self._queries += 1 return self._bugs_affecting_package[package]
def test_get_bugs_list(): """older versions of python-debianbts accepted malformed key-val-lists.""" bugs = bts.get_bugs( 'submitter', '*****@*****.**', 'severity', 'normal') bugs2 = bts.get_bugs( ['submitter', '*****@*****.**', 'severity', 'normal']) assert len(bugs) != 0 bugs.sort() bugs2.sort() assert bugs == bugs2
def test_get_bugs(caplog): """get_bugs should return list of bugnumbers.""" bugs = bts.get_bugs(submitter="*****@*****.**") assert len(bugs) != 0 assert isinstance(bugs, list) for i in bugs: assert isinstance(i, int) bugs = bts.get_bugs("submitter", "*****@*****.**") assert len(bugs) != 0 assert isinstance(bugs, list) for i in bugs: assert isinstance(i, int) assert "deprecated" in caplog.text
def _get_bugs_thread(self, pkg): try: bugs = bts.get_bugs('package', pkg) except Exception as exc: self._thread_failed = True print('threaded get_bugs() call failed ' 'with exception {} {}'.format(type(exc), exc))
def test_get_bugs(self): """get_bugs should return list of bugnumbers.""" l = bts.get_bugs("submitter", "*****@*****.**") self.assertFalse(len(l) == 0) self.assertEqual(type(l), type([])) for i in l: self.assertEqual(type(i), type(int()))
def test_get_bugs(): """get_bugs should return list of bugnumbers.""" bugs = bts.get_bugs("submitter", "*****@*****.**") assert len(bugs) != 0 assert isinstance(bugs, list) for i in bugs: assert isinstance(i, int)
def test_get_bugs_list(caplog): """older versions of python-debianbts accepted malformed key-val-lists.""" bugs = bts.get_bugs(submitter='*****@*****.**', severity='normal') assert len(bugs) != 0 bugs = bts.get_bugs( 'submitter', '*****@*****.**', 'severity', 'normal') bugs2 = bts.get_bugs( ['submitter', '*****@*****.**', 'severity', 'normal']) assert len(bugs) != 0 bugs.sort() bugs2.sort() assert bugs == bugs2 assert "deprecated" in caplog.text
def unclassified_bugs(self): """ Returns a list of open bugs which have not yet been classified by one of our usertags. """ tagged_bugs = bts.get_usertag('*****@*****.**') tagged_bugs_ftp = [] for tags in tagged_bugs.keys(): tagged_bugs_ftp += tagged_bugs[tags] return [bug for bug in bts.get_status(bts.get_bugs("package", "ftp.debian.org")) if bug.pending == 'pending' and bug.bug_num not in tagged_bugs_ftp]
def search(self, sources, bug_numbers=None): if bug_numbers is None: bug_numbers = set() else: bug_numbers = set(bug_numbers) bug_numbers.update(debianbts.get_bugs('src', sources)) # process in batches. stolen from python-debianbts. BATCH_SIZE = 500 result = [] for i in range(0, len(bug_numbers), BATCH_SIZE): slice = list(bug_numbers)[i:i + BATCH_SIZE] result += debianbts.get_status(slice) return [ _DebianBugReport(b, self.binary_source_map, self.ignore_unknown_binaries) for b in result ]
def piuparts_bugs_affecting(package): bugs = debianbts.get_bugs('affects', package, 'bugs', all_piuparts_bugs(), 'archive', 'both') bugs += debianbts.get_bugs('affects', 'src:' + package, 'bugs', all_piuparts_bugs(), 'archive', 'both') return sorted(set(bugs), reverse=True)
def remove(session, reason, suites, removals, whoami=None, partial=False, components=None, done_bugs=None, date=None, carbon_copy=None, close_related_bugs=False): """Batch remove a number of packages Verify that the files listed in the Files field of the .dsc are those expected given the announced Format. @type session: SQLA Session @param session: The database session in use @type reason: string @param reason: The reason for the removal (e.g. "[auto-cruft] NBS (no longer built by <source>)") @type suites: list @param suites: A list of the suite names in which the removal should occur @type removals: list @param removals: A list of the removals. Each element should be a tuple (or list) of at least the following for 4 items from the database (in order): package, version, architecture, (database) id. For source packages, the "architecture" should be set to "source". @type partial: bool @param partial: Whether the removal is "partial" (e.g. architecture specific). @type components: list @param components: List of components involved in a partial removal. Can be an empty list to not restrict the removal to any components. @type whoami: string @param whoami: The person (or entity) doing the removal. Defaults to utils.whoami() @type date: string @param date: The date of the removal. Defaults to commands.getoutput("date -R") @type done_bugs: list @param done_bugs: A list of bugs to be closed when doing this removal. @type close_related_bugs: bool @param done_bugs: Whether bugs related to the package being removed should be closed as well. NB: Not implemented for more than one suite. @type carbon_copy: list @param carbon_copy: A list of mail addresses to CC when doing removals. NB: all items are taken "as-is" unlike "dak rm". @rtype: None @return: Nothing """ # Generate the summary of what's to be removed d = {} summary = "" sources = [] binaries = [] whitelists = [] versions = [] suite_ids_list = [] suites_list = utils.join_with_commas_and(suites) cnf = utils.get_conf() con_components = '' ####################################################################################################### if not reason: raise ValueError("Empty removal reason not permitted") if not removals: raise ValueError("Nothing to remove!?") if not suites: raise ValueError("Removals without a suite!?") if whoami is None: whoami = utils.whoami() if date is None: date = commands.getoutput("date -R") if partial and components: component_ids_list = [] for componentname in components: component = get_component(componentname, session=session) if component is None: raise ValueError("component '%s' not recognised." % componentname) else: component_ids_list.append(component.component_id) if component_ids_list: con_components = "AND component IN (%s)" % ", ".join( [str(i) for i in component_ids_list]) for i in removals: package = i[0] version = i[1] architecture = i[2] if package not in d: d[package] = {} if version not in d[package]: d[package][version] = [] if architecture not in d[package][version]: d[package][version].append(architecture) for package in sorted(d): versions = sorted(d[package], cmp=apt_pkg.version_compare) for version in versions: d[package][version].sort(utils.arch_compare_sw) summary += "%10s | %10s | %s\n" % (package, version, ", ".join( d[package][version])) for package in summary.split("\n"): for row in package.split("\n"): element = row.split("|") if len(element) == 3: if element[2].find("source") > 0: sources.append( "%s_%s" % tuple(elem.strip(" ") for elem in element[:2])) element[2] = sub("source\s?,?", "", element[2]).strip(" ") if element[2]: binaries.append("%s_%s [%s]" % tuple(elem.strip(" ") for elem in element)) dsc_type_id = get_override_type('dsc', session).overridetype_id deb_type_id = get_override_type('deb', session).overridetype_id for suite in suites: s = get_suite(suite, session=session) if s is not None: suite_ids_list.append(s.suite_id) whitelists.append(s.mail_whitelist) ####################################################################################################### log_filename = cnf["Rm::LogFile"] log822_filename = cnf["Rm::LogFile822"] with utils.open_file(log_filename, "a") as logfile, utils.open_file( log822_filename, "a") as logfile822: fcntl.lockf(logfile, fcntl.LOCK_EX) fcntl.lockf(logfile822, fcntl.LOCK_EX) logfile.write( "=========================================================================\n" ) logfile.write("[Date: %s] [ftpmaster: %s]\n" % (date, whoami)) logfile.write("Removed the following packages from %s:\n\n%s" % (suites_list, summary)) if done_bugs: logfile.write("Closed bugs: %s\n" % (", ".join(done_bugs))) logfile.write( "\n------------------- Reason -------------------\n%s\n" % reason) logfile.write("----------------------------------------------\n") logfile822.write("Date: %s\n" % date) logfile822.write("Ftpmaster: %s\n" % whoami) logfile822.write("Suite: %s\n" % suites_list) if sources: logfile822.write("Sources:\n") for source in sources: logfile822.write(" %s\n" % source) if binaries: logfile822.write("Binaries:\n") for binary in binaries: logfile822.write(" %s\n" % binary) logfile822.write("Reason: %s\n" % reason.replace('\n', '\n ')) if done_bugs: logfile822.write("Bug: %s\n" % (", ".join(done_bugs))) for i in removals: package = i[0] architecture = i[2] package_id = i[3] for suite_id in suite_ids_list: if architecture == "source": session.execute( "DELETE FROM src_associations WHERE source = :packageid AND suite = :suiteid", { 'packageid': package_id, 'suiteid': suite_id }) else: session.execute( "DELETE FROM bin_associations WHERE bin = :packageid AND suite = :suiteid", { 'packageid': package_id, 'suiteid': suite_id }) # Delete from the override file if not partial: if architecture == "source": type_id = dsc_type_id else: type_id = deb_type_id # TODO: Fix this properly to remove the remaining non-bind argument session.execute( "DELETE FROM override WHERE package = :package AND type = :typeid AND suite = :suiteid %s" % (con_components), { 'package': package, 'typeid': type_id, 'suiteid': suite_id }) session.commit() # ### REMOVAL COMPLETE - send mail time ### # # If we don't have a Bug server configured, we're done if "Dinstall::BugServer" not in cnf: if done_bugs or close_related_bugs: utils.warn( "Cannot send mail to BugServer as Dinstall::BugServer is not configured" ) logfile.write( "=========================================================================\n" ) logfile822.write("\n") return # read common subst variables for all bug closure mails Subst_common = {} Subst_common["__RM_ADDRESS__"] = cnf["Dinstall::MyEmailAddress"] Subst_common["__BUG_SERVER__"] = cnf["Dinstall::BugServer"] Subst_common["__CC__"] = "X-DAK: dak rm" if carbon_copy: Subst_common["__CC__"] += "\nCc: " + ", ".join(carbon_copy) Subst_common["__SUITE_LIST__"] = suites_list Subst_common["__SUBJECT__"] = "Removed package(s) from %s" % ( suites_list) Subst_common["__ADMIN_ADDRESS__"] = cnf["Dinstall::MyAdminAddress"] Subst_common["__DISTRO__"] = cnf["Dinstall::MyDistribution"] Subst_common["__WHOAMI__"] = whoami # Send the bug closing messages if done_bugs: Subst_close_rm = Subst_common bcc = [] if cnf.find("Dinstall::Bcc") != "": bcc.append(cnf["Dinstall::Bcc"]) if cnf.find("Rm::Bcc") != "": bcc.append(cnf["Rm::Bcc"]) if bcc: Subst_close_rm["__BCC__"] = "Bcc: " + ", ".join(bcc) else: Subst_close_rm["__BCC__"] = "X-Filler: 42" summarymail = "%s\n------------------- Reason -------------------\n%s\n" % ( summary, reason) summarymail += "----------------------------------------------\n" Subst_close_rm["__SUMMARY__"] = summarymail for bug in done_bugs: Subst_close_rm["__BUG_NUMBER__"] = bug if close_related_bugs: mail_message = utils.TemplateSubst( Subst_close_rm, cnf["Dir::Templates"] + "/rm.bug-close-with-related") else: mail_message = utils.TemplateSubst( Subst_close_rm, cnf["Dir::Templates"] + "/rm.bug-close") utils.send_mail(mail_message, whitelists=whitelists) # close associated bug reports if close_related_bugs: Subst_close_other = Subst_common bcc = [] wnpp = utils.parse_wnpp_bug_file() versions = list(set([re_bin_only_nmu.sub('', v) for v in versions])) if len(versions) == 1: Subst_close_other["__VERSION__"] = versions[0] else: logfile.write( "=========================================================================\n" ) logfile822.write("\n") raise ValueError( "Closing bugs with multiple package versions is not supported. Do it yourself." ) if bcc: Subst_close_other["__BCC__"] = "Bcc: " + ", ".join(bcc) else: Subst_close_other["__BCC__"] = "X-Filler: 42" # at this point, I just assume, that the first closed bug gives # some useful information on why the package got removed Subst_close_other["__BUG_NUMBER__"] = done_bugs[0] if len(sources) == 1: source_pkg = source.split("_", 1)[0] else: logfile.write( "=========================================================================\n" ) logfile822.write("\n") raise ValueError( "Closing bugs for multiple source packages is not supported. Please do it yourself." ) Subst_close_other["__BUG_NUMBER_ALSO__"] = "" Subst_close_other["__SOURCE__"] = source_pkg merged_bugs = set() other_bugs = bts.get_bugs('src', source_pkg, 'status', 'open', 'status', 'forwarded') if other_bugs: for bugno in other_bugs: if bugno not in merged_bugs: for bug in bts.get_status(bugno): for merged in bug.mergedwith: other_bugs.remove(merged) merged_bugs.add(merged) logfile.write("Also closing bug(s):") logfile822.write("Also-Bugs:") for bug in other_bugs: Subst_close_other["__BUG_NUMBER_ALSO__"] += str( bug) + "-done@" + cnf["Dinstall::BugServer"] + "," logfile.write(" " + str(bug)) logfile822.write(" " + str(bug)) logfile.write("\n") logfile822.write("\n") if source_pkg in wnpp: logfile.write("Also closing WNPP bug(s):") logfile822.write("Also-WNPP:") for bug in wnpp[source_pkg]: # the wnpp-rm file we parse also contains our removal # bugs, filtering that out if bug != Subst_close_other["__BUG_NUMBER__"]: Subst_close_other["__BUG_NUMBER_ALSO__"] += str( bug) + "-done@" + cnf["Dinstall::BugServer"] + "," logfile.write(" " + str(bug)) logfile822.write(" " + str(bug)) logfile.write("\n") logfile822.write("\n") mail_message = utils.TemplateSubst( Subst_close_other, cnf["Dir::Templates"] + "/rm.bug-close-related") if Subst_close_other["__BUG_NUMBER_ALSO__"]: utils.send_mail(mail_message) logfile.write( "=========================================================================\n" ) logfile822.write("\n")
def piuparts_bugs_affecting(package): bugs = debianbts.get_bugs('affects', package, 'bugs', all_piuparts_bugs(), 'archive', 'both') bugs.sort(reverse=True) return bugs
def piuparts_bugs_in(package): return debianbts.get_bugs("package", package, "bugs", all_piuparts_bugs()) + debianbts.get_bugs( "affects", package, "bugs", all_piuparts_bugs() )
def get_reports(package, timeout, system='debian', mirrors=None, version=None, http_proxy='', archived=False, source=False): if system == 'debian': if isinstance(package, basestring): if source: pkg_filter = 'src' else: pkg_filter = 'package' bugs = debianbts.get_bugs(pkg_filter, package) else: bugs = map(int, package) # retrieve bugs and generate the hierarchy stats = debianbts.get_status(bugs) d = defaultdict(list) for s in stats: # We now return debianbts.Bugreport objects, containing all the info # for a bug, so UIs can extract them as needed d[s.severity].append(s) # keep the bugs ordered per severity # XXX: shouldn't it be something UI-related? # # The hierarchy is a list of tuples: # (description of the severity, list of bugs for that severity) hier = [] for sev in SEVLIST: if sev in d: hier.append(('Bugs with severity %s' % sev, d[sev])) return (len(bugs), 'Bug reports for %s' % package, hier) # XXX: is the code below used at all now? can we remove it? if isinstance(package, basestring): if SYSTEMS[system].get('cgiroot'): try: result = get_cgi_reports(package, timeout, system, http_proxy, archived, source, version=version) except: raise NoNetwork if result: return result url = package_url(system, package, mirrors, source) try: page = open_url(url, http_proxy, timeout) except: raise NoNetwork if not page: return (0, None, None) #content = page.read() #if 'Maintainer' not in content: # return (0, None, None) parser = BTSParser() for line in page: parser.feed(line) parser.close() try: page.fp._sock.recv = None except: pass page.close() return parser.bugcount, parser.title, parser.hierarchy # A list of bug numbers this_hierarchy = [] package = [int(x) for x in package] package.sort() for bug in package: result = get_report(bug, timeout, system, mirrors, http_proxy, archived) if result: title, body = result this_hierarchy.append(title) #print title title = "Multiple bug reports" bugcount = len(this_hierarchy) hierarchy = [('Reports', this_hierarchy)] return bugcount, title, hierarchy
def test_get_bugs_single_int_bug(): """bugs parameter in get_bugs can be a list of int or a int""" bugs1 = bts.get_bugs('bugs', 400040, 'archive', True) bugs2 = bts.get_bugs('bugs', [400040], 'archive', True) assert bugs1 == bugs2
def testGetBugsEmpty(self): """get_bugs should return empty list if no matching bugs where found.""" l = bts.get_bugs("package", "thisisatest") self.assertEqual(l, [])
def test_get_bugs_empty(self): """get_bugs should return empty list if no matching bugs where found.""" l = bts.get_bugs("package", "thisisatest") self.assertEqual(l, [])
def testGetBugs(self): """get_bugs should return list of bugnumbers.""" l = bts.get_bugs("owner", "*****@*****.**") self.assertEqual(type(l), type([])) for i in l: self.assertEqual(type(i), type(int()))
def test_get_bugs_single_int_bug(self): """bugs parameter in get_bugs can be a list of int or a int""" bugs1 = bts.get_bugs('bugs', 400040, 'archive', True) bugs2 = bts.get_bugs('bugs', [400040], 'archive', True) self.assertEquals(bugs1, bugs2)
def test_get_bugs_int_bugs(self): """It is possible to pass a list of bug number to get_bugs""" bugs = bts.get_bugs('bugs', [400010, 400012], 'archive', True) self.assertEquals(set(bugs), set((400010, 400012)))
def remove(session, reason, suites, removals, whoami=None, partial=False, components=None, done_bugs=None, date=None, carbon_copy=None, close_related_bugs=False): """Batch remove a number of packages Verify that the files listed in the Files field of the .dsc are those expected given the announced Format. @type session: SQLA Session @param session: The database session in use @type reason: string @param reason: The reason for the removal (e.g. "[auto-cruft] NBS (no longer built by <source>)") @type suites: list @param suites: A list of the suite names in which the removal should occur @type removals: list @param removals: A list of the removals. Each element should be a tuple (or list) of at least the following for 4 items from the database (in order): package, version, architecture, (database) id. For source packages, the "architecture" should be set to "source". @type partial: bool @param partial: Whether the removal is "partial" (e.g. architecture specific). @type components: list @param components: List of components involved in a partial removal. Can be an empty list to not restrict the removal to any components. @type whoami: string @param whoami: The person (or entity) doing the removal. Defaults to utils.whoami() @type date: string @param date: The date of the removal. Defaults to commands.getoutput("date -R") @type done_bugs: list @param done_bugs: A list of bugs to be closed when doing this removal. @type close_related_bugs: bool @param done_bugs: Whether bugs related to the package being removed should be closed as well. NB: Not implemented for more than one suite. @type carbon_copy: list @param carbon_copy: A list of mail addresses to CC when doing removals. NB: all items are taken "as-is" unlike "dak rm". @rtype: None @return: Nothing """ # Generate the summary of what's to be removed d = {} summary = "" sources = [] binaries = [] whitelists = [] versions = [] suite_ids_list = [] suites_list = utils.join_with_commas_and(suites) cnf = utils.get_conf() con_components = '' ####################################################################################################### if not reason: raise ValueError("Empty removal reason not permitted") if not removals: raise ValueError("Nothing to remove!?") if not suites: raise ValueError("Removals without a suite!?") if whoami is None: whoami = utils.whoami() if date is None: date = commands.getoutput("date -R") if partial and components: component_ids_list = [] for componentname in components: component = get_component(componentname, session=session) if component is None: raise ValueError("component '%s' not recognised." % componentname) else: component_ids_list.append(component.component_id) if component_ids_list: con_components = "AND component IN (%s)" % ", ".join([str(i) for i in component_ids_list]) for i in removals: package = i[0] version = i[1] architecture = i[2] if package not in d: d[package] = {} if version not in d[package]: d[package][version] = [] if architecture not in d[package][version]: d[package][version].append(architecture) for package in sorted(d): versions = sorted(d[package], cmp=apt_pkg.version_compare) for version in versions: d[package][version].sort(utils.arch_compare_sw) summary += "%10s | %10s | %s\n" % (package, version, ", ".join(d[package][version])) for package in summary.split("\n"): for row in package.split("\n"): element = row.split("|") if len(element) == 3: if element[2].find("source") > 0: sources.append("%s_%s" % tuple(elem.strip(" ") for elem in element[:2])) element[2] = sub("source\s?,?", "", element[2]).strip(" ") if element[2]: binaries.append("%s_%s [%s]" % tuple(elem.strip(" ") for elem in element)) dsc_type_id = get_override_type('dsc', session).overridetype_id deb_type_id = get_override_type('deb', session).overridetype_id for suite in suites: s = get_suite(suite, session=session) if s is not None: suite_ids_list.append(s.suite_id) whitelists.append(s.mail_whitelist) ####################################################################################################### log_filename = cnf["Rm::LogFile"] log822_filename = cnf["Rm::LogFile822"] with utils.open_file(log_filename, "a") as logfile, utils.open_file(log822_filename, "a") as logfile822: fcntl.lockf(logfile, fcntl.LOCK_EX) fcntl.lockf(logfile822, fcntl.LOCK_EX) logfile.write("=========================================================================\n") logfile.write("[Date: %s] [ftpmaster: %s]\n" % (date, whoami)) logfile.write("Removed the following packages from %s:\n\n%s" % (suites_list, summary)) if done_bugs: logfile.write("Closed bugs: %s\n" % (", ".join(done_bugs))) logfile.write("\n------------------- Reason -------------------\n%s\n" % reason) logfile.write("----------------------------------------------\n") logfile822.write("Date: %s\n" % date) logfile822.write("Ftpmaster: %s\n" % whoami) logfile822.write("Suite: %s\n" % suites_list) if sources: logfile822.write("Sources:\n") for source in sources: logfile822.write(" %s\n" % source) if binaries: logfile822.write("Binaries:\n") for binary in binaries: logfile822.write(" %s\n" % binary) logfile822.write("Reason: %s\n" % reason.replace('\n', '\n ')) if done_bugs: logfile822.write("Bug: %s\n" % (", ".join(done_bugs))) for i in removals: package = i[0] architecture = i[2] package_id = i[3] for suite_id in suite_ids_list: if architecture == "source": session.execute("DELETE FROM src_associations WHERE source = :packageid AND suite = :suiteid", {'packageid': package_id, 'suiteid': suite_id}) else: session.execute("DELETE FROM bin_associations WHERE bin = :packageid AND suite = :suiteid", {'packageid': package_id, 'suiteid': suite_id}) # Delete from the override file if not partial: if architecture == "source": type_id = dsc_type_id else: type_id = deb_type_id # TODO: Fix this properly to remove the remaining non-bind argument session.execute("DELETE FROM override WHERE package = :package AND type = :typeid AND suite = :suiteid %s" % (con_components), {'package': package, 'typeid': type_id, 'suiteid': suite_id}) session.commit() # ### REMOVAL COMPLETE - send mail time ### # # If we don't have a Bug server configured, we're done if "Dinstall::BugServer" not in cnf: if done_bugs or close_related_bugs: utils.warn("Cannot send mail to BugServer as Dinstall::BugServer is not configured") logfile.write("=========================================================================\n") logfile822.write("\n") return # read common subst variables for all bug closure mails Subst_common = {} Subst_common["__RM_ADDRESS__"] = cnf["Dinstall::MyEmailAddress"] Subst_common["__BUG_SERVER__"] = cnf["Dinstall::BugServer"] Subst_common["__CC__"] = "X-DAK: dak rm" if carbon_copy: Subst_common["__CC__"] += "\nCc: " + ", ".join(carbon_copy) Subst_common["__SUITE_LIST__"] = suites_list Subst_common["__SUBJECT__"] = "Removed package(s) from %s" % (suites_list) Subst_common["__ADMIN_ADDRESS__"] = cnf["Dinstall::MyAdminAddress"] Subst_common["__DISTRO__"] = cnf["Dinstall::MyDistribution"] Subst_common["__WHOAMI__"] = whoami # Send the bug closing messages if done_bugs: Subst_close_rm = Subst_common bcc = [] if cnf.find("Dinstall::Bcc") != "": bcc.append(cnf["Dinstall::Bcc"]) if cnf.find("Rm::Bcc") != "": bcc.append(cnf["Rm::Bcc"]) if bcc: Subst_close_rm["__BCC__"] = "Bcc: " + ", ".join(bcc) else: Subst_close_rm["__BCC__"] = "X-Filler: 42" summarymail = "%s\n------------------- Reason -------------------\n%s\n" % (summary, reason) summarymail += "----------------------------------------------\n" Subst_close_rm["__SUMMARY__"] = summarymail for bug in done_bugs: Subst_close_rm["__BUG_NUMBER__"] = bug if close_related_bugs: mail_message = utils.TemplateSubst(Subst_close_rm,cnf["Dir::Templates"]+"/rm.bug-close-with-related") else: mail_message = utils.TemplateSubst(Subst_close_rm,cnf["Dir::Templates"]+"/rm.bug-close") utils.send_mail(mail_message, whitelists=whitelists) # close associated bug reports if close_related_bugs: Subst_close_other = Subst_common bcc = [] wnpp = utils.parse_wnpp_bug_file() versions = list(set([re_bin_only_nmu.sub('', v) for v in versions])) if len(versions) == 1: Subst_close_other["__VERSION__"] = versions[0] else: logfile.write("=========================================================================\n") logfile822.write("\n") raise ValueError("Closing bugs with multiple package versions is not supported. Do it yourself.") if bcc: Subst_close_other["__BCC__"] = "Bcc: " + ", ".join(bcc) else: Subst_close_other["__BCC__"] = "X-Filler: 42" # at this point, I just assume, that the first closed bug gives # some useful information on why the package got removed Subst_close_other["__BUG_NUMBER__"] = done_bugs[0] if len(sources) == 1: source_pkg = source.split("_", 1)[0] else: logfile.write("=========================================================================\n") logfile822.write("\n") raise ValueError("Closing bugs for multiple source packages is not supported. Please do it yourself.") Subst_close_other["__BUG_NUMBER_ALSO__"] = "" Subst_close_other["__SOURCE__"] = source_pkg merged_bugs = set() other_bugs = bts.get_bugs('src', source_pkg, 'status', 'open', 'status', 'forwarded') if other_bugs: for bugno in other_bugs: if bugno not in merged_bugs: for bug in bts.get_status(bugno): for merged in bug.mergedwith: other_bugs.remove(merged) merged_bugs.add(merged) logfile.write("Also closing bug(s):") logfile822.write("Also-Bugs:") for bug in other_bugs: Subst_close_other["__BUG_NUMBER_ALSO__"] += str(bug) + "-done@" + cnf["Dinstall::BugServer"] + "," logfile.write(" " + str(bug)) logfile822.write(" " + str(bug)) logfile.write("\n") logfile822.write("\n") if source_pkg in wnpp: logfile.write("Also closing WNPP bug(s):") logfile822.write("Also-WNPP:") for bug in wnpp[source_pkg]: # the wnpp-rm file we parse also contains our removal # bugs, filtering that out if bug != Subst_close_other["__BUG_NUMBER__"]: Subst_close_other["__BUG_NUMBER_ALSO__"] += str(bug) + "-done@" + cnf["Dinstall::BugServer"] + "," logfile.write(" " + str(bug)) logfile822.write(" " + str(bug)) logfile.write("\n") logfile822.write("\n") mail_message = utils.TemplateSubst(Subst_close_other, cnf["Dir::Templates"]+"/rm.bug-close-related") if Subst_close_other["__BUG_NUMBER_ALSO__"]: utils.send_mail(mail_message) logfile.write("=========================================================================\n") logfile822.write("\n")
def test_get_bugs_int_bugs(): """It is possible to pass a list of bug number to get_bugs""" bugs = bts.get_bugs('bugs', [400010, 400012], 'archive', True) assert set(bugs) == set((400010, 400012))
def _get_bugs_thread(self, pkg): try: bts.get_bugs('package', pkg) except Exception: self._thread_failed = True logger.exception('Threaded get_bugs() call failed.')
def test_get_bugs_empty(): """get_bugs should return empty list if no matching bugs where found.""" bugs = bts.get_bugs("package", "thisisatest") assert bugs == []
def issues(self): # Initialise empty list of bug numbers collected_bugs = [] # Search BTS for bugs owned by email address if self.email: owned_bugs = debianbts.get_bugs("owner", self.email, "status", "open") collected_bugs.extend(owned_bugs) # Search BTS for bugs related to specified packages if self.packages: packages = self.packages.split(",") for pkg in packages: pkg_bugs = debianbts.get_bugs("package", pkg, "status", "open") for bug in pkg_bugs: if bug not in collected_bugs: collected_bugs.append(bug) # Search UDD bugs search for bugs belonging to packages that # are maintained by the email address if self.udd: udd_bugs = self._get_udd_bugs() for bug in udd_bugs: if bug not in collected_bugs: collected_bugs.append(bug['id']) issues = [ self._record_for_bug(bug) for bug in debianbts.get_status(collected_bugs) ] log.debug(" Found %i total.", len(issues)) if self.ignore_pkg: ignore_pkg = self.ignore_pkg.split(",") for pkg in ignore_pkg: issues = [ issue for issue in issues if not issue['package'] == pkg ] if self.ignore_src: ignore_src = self.ignore_src.split(",") for src in ignore_src: issues = [ issue for issue in issues if not issue['source'] == src ] if self.ignore_pending: issues = [ issue for issue in issues if not issue['status'] == 'pending-fixed' ] issues = [ issue for issue in issues if not (issue['status'] == 'done' or issue['status'] == 'fixed') ] log.debug(" Pruned down to %i.", len(issues)) for issue in issues: issue_obj = self.get_issue_for_record(issue) extra = {'annotations': self.annotations(issue, issue_obj)} issue_obj.update_extra(extra) yield issue_obj
def lineedit_return_pressed(self): # # just in case ;) # text = unicode(self.lineEdit.text()) if text.startswith("http://"): self._show_url(text) return if len(text) == 0: return self.logger.info("Return pressed.") self.lineEdit.clear() # TODO: self.lineEdit.clear() does not always work, why? #QtCore.QTimer.singleShot(0,self.lineEdit,QtCore.SLOT("clear()")) query = rng.translate_query(text) self.logger.debug("Query: %s" % str(query)) # test if there is a submit-as field available and rename the packages # if nececesairy for i in range(0, len(query), 2): if query[i] == 'package': realname = bug.submit_as(query[i+1]) if query[i+1] != realname: self.logger.debug("Using %s as package name as requested by developer." % str(realname)) query[i+1] = realname # Single bug or list of bugs? if query[0]: buglist = bts.get_bugs(query) else: buglist = [query[1]] # ok, we know the package, so enable some buttons which don't depend # on the existence of the acutal packe (wnpp) or bugreports for that # package. if query[0] in ("src", "package"): self._stateChanged(query[1], None) # if we got a bugnumber we'd like to select it and enable some more # buttons. unfortunately we don't know if the bugnumber actually exists # for now, so we have to wait a bit until the bug is fetched. else: self._stateChanged(None, None) self.logger.debug("Buglist matching the query: %s" % str(buglist)) chunksize = 50 if len(buglist) > chunksize: self.load_started() self.logger.debug("Buglist longer than %i, splitting in chunks." % chunksize) self.bugs = [] i = 0 for chunk in chunks(buglist, chunksize): i += 1 progress = int(100. * i * chunksize / len(buglist)) if progress > 100: progress = 100 self.load_progress(progress) bl = bts.get_status(chunk) if len(bl) == 0: self.logger.error("One of the following bugs caused the BTS to hickup: %s" % str(bl)) self.bugs.extend(bl) self.load_finished(True) else: self.bugs = bts.get_status(buglist) # ok, we fetched the bugs. see if the list isn't empty if query[0] in (None,) and len(self.bugs) > 0: self.currentBug = self.bugs[0] self.currentPackage = self.currentBug.package self._stateChanged(self.currentPackage, self.currentBug) self.model.set_elements(self.bugs) self.tableView.resizeRowsToContents()
def get_reports(package, timeout, system='debian', mirrors=None, version=None, http_proxy='', archived=False, source=False): if system == 'debian': if isinstance(package, str): if source: pkg_filter = 'src' else: pkg_filter = 'package' bugs = debianbts.get_bugs(pkg_filter, package) else: bugs = list(map(int, package)) try: # retrieve bugs and generate the hierarchy stats = debianbts.get_status(bugs) except: raise QuertBTSError d = defaultdict(list) for s in stats: # We now return debianbts.Bugreport objects, containing all the info # for a bug, so UIs can extract them as needed d[s.severity].append(s) # keep the bugs ordered per severity # XXX: shouldn't it be something UI-related? # # The hierarchy is a list of tuples: # (description of the severity, list of bugs for that severity) hier = [] for sev in SEVLIST: if sev in d: hier.append(('Bugs with severity %s' % sev, d[sev])) return (len(bugs), 'Bug reports for %s' % package, hier) # XXX: is the code below used at all now? can we remove it? if isinstance(package, str): if SYSTEMS[system].get('cgiroot'): try: result = get_cgi_reports(package, timeout, system, http_proxy, archived, source, version=version) except: raise NoNetwork if result: return result url = package_url(system, package, mirrors, source) try: page = open_url(url, http_proxy, timeout) except: raise NoNetwork if not page: return (0, None, None) # content = page.read() # if 'Maintainer' not in content: # return (0, None, None) parser = BTSParser() for line in page.splitlines(): parser.feed(line) parser.close() return parser.bugcount, parser.title, parser.hierarchy # A list of bug numbers this_hierarchy = [] package = [int(x) for x in package] package.sort() for bug in package: result = get_report(bug, timeout, system, mirrors, http_proxy, archived) if result: title, body = result this_hierarchy.append(title) # print title title = "Multiple bug reports" bugcount = len(this_hierarchy) hierarchy = [('Reports', this_hierarchy)] return bugcount, title, hierarchy
def fetch_bug_numbers_by_package(pkg_name): """Fetch non-archived bugs""" return debianbts.get_bugs('package', pkg_name, 'archive', 'false')
def main (): global Options cnf = Config() Arguments = [('h',"help","Rm::Options::Help"), ('a',"architecture","Rm::Options::Architecture", "HasArg"), ('b',"binary", "Rm::Options::Binary"), ('B',"binary-only", "Rm::Options::Binary-Only"), ('c',"component", "Rm::Options::Component", "HasArg"), ('C',"carbon-copy", "Rm::Options::Carbon-Copy", "HasArg"), # Bugs to Cc ('d',"done","Rm::Options::Done", "HasArg"), # Bugs fixed ('D',"do-close","Rm::Options::Do-Close"), ('R',"rdep-check", "Rm::Options::Rdep-Check"), ('m',"reason", "Rm::Options::Reason", "HasArg"), # Hysterical raisins; -m is old-dinstall option for rejection reason ('n',"no-action","Rm::Options::No-Action"), ('p',"partial", "Rm::Options::Partial"), ('s',"suite","Rm::Options::Suite", "HasArg"), ('S',"source-only", "Rm::Options::Source-Only"), ] for i in [ "architecture", "binary", "binary-only", "carbon-copy", "component", "done", "help", "no-action", "partial", "rdep-check", "reason", "source-only", "Do-Close" ]: if not cnf.has_key("Rm::Options::%s" % (i)): cnf["Rm::Options::%s" % (i)] = "" if not cnf.has_key("Rm::Options::Suite"): cnf["Rm::Options::Suite"] = "unstable" arguments = apt_pkg.parse_commandline(cnf.Cnf, Arguments, sys.argv) Options = cnf.subtree("Rm::Options") if Options["Help"]: usage() session = DBConn().session() # Sanity check options if not arguments: utils.fubar("need at least one package name as an argument.") if Options["Architecture"] and Options["Source-Only"]: utils.fubar("can't use -a/--architecture and -S/--source-only options simultaneously.") if ((Options["Binary"] and Options["Source-Only"]) or (Options["Binary"] and Options["Binary-Only"]) or (Options["Binary-Only"] and Options["Source-Only"])): utils.fubar("Only one of -b/--binary, -B/--binary-only and -S/--source-only can be used.") if Options.has_key("Carbon-Copy") and not Options.has_key("Done"): utils.fubar("can't use -C/--carbon-copy without also using -d/--done option.") if Options["Architecture"] and not Options["Partial"]: utils.warn("-a/--architecture implies -p/--partial.") Options["Partial"] = "true" if Options["Do-Close"] and not Options["Done"]: utils.fubar("No.") if (Options["Do-Close"] and (Options["Binary"] or Options["Binary-Only"] or Options["Source-Only"])): utils.fubar("No.") # Force the admin to tell someone if we're not doing a 'dak # cruft-report' inspired removal (or closing a bug, which counts # as telling someone). if not Options["No-Action"] and not Options["Carbon-Copy"] \ and not Options["Done"] and Options["Reason"].find("[auto-cruft]") == -1: utils.fubar("Need a -C/--carbon-copy if not closing a bug and not doing a cruft removal.") # Process -C/--carbon-copy # # Accept 3 types of arguments (space separated): # 1) a number - assumed to be a bug number, i.e. [email protected] # 2) the keyword 'package' - cc's [email protected] for every argument # 3) contains a '@' - assumed to be an email address, used unmofidied # carbon_copy = [] for copy_to in utils.split_args(Options.get("Carbon-Copy")): if copy_to.isdigit(): if cnf.has_key("Dinstall::BugServer"): carbon_copy.append(copy_to + "@" + cnf["Dinstall::BugServer"]) else: utils.fubar("Asked to send mail to #%s in BTS but Dinstall::BugServer is not configured" % copy_to) elif copy_to == 'package': for package in arguments: if cnf.has_key("Dinstall::PackagesServer"): carbon_copy.append(package + "@" + cnf["Dinstall::PackagesServer"]) if cnf.has_key("Dinstall::TrackingServer"): carbon_copy.append(package + "@" + cnf["Dinstall::TrackingServer"]) elif '@' in copy_to: carbon_copy.append(copy_to) else: utils.fubar("Invalid -C/--carbon-copy argument '%s'; not a bug number, 'package' or email address." % (copy_to)) if Options["Binary"]: field = "b.package" else: field = "s.source" con_packages = "AND %s IN (%s)" % (field, ", ".join([ repr(i) for i in arguments ])) (con_suites, con_architectures, con_components, check_source) = \ utils.parse_args(Options) # Additional suite checks suite_ids_list = [] whitelists = [] suites = utils.split_args(Options["Suite"]) suites_list = utils.join_with_commas_and(suites) if not Options["No-Action"]: for suite in suites: s = get_suite(suite, session=session) if s is not None: suite_ids_list.append(s.suite_id) whitelists.append(s.mail_whitelist) if suite in ("oldstable", "stable"): print "**WARNING** About to remove from the (old)stable suite!" print "This should only be done just prior to a (point) release and not at" print "any other time." game_over() elif suite == "testing": print "**WARNING About to remove from the testing suite!" print "There's no need to do this normally as removals from unstable will" print "propogate to testing automagically." game_over() # Additional architecture checks if Options["Architecture"] and check_source: utils.warn("'source' in -a/--argument makes no sense and is ignored.") # Additional component processing over_con_components = con_components.replace("c.id", "component") # Don't do dependency checks on multiple suites if Options["Rdep-Check"] and len(suites) > 1: utils.fubar("Reverse dependency check on multiple suites is not implemented.") to_remove = [] maintainers = {} # We have 3 modes of package selection: binary, source-only, binary-only # and source+binary. # XXX: TODO: This all needs converting to use placeholders or the object # API. It's an SQL injection dream at the moment if Options["Binary"]: # Removal by binary package name q = session.execute("SELECT b.package, b.version, a.arch_string, b.id, b.maintainer FROM binaries b, bin_associations ba, architecture a, suite su, files f, files_archive_map af, component c WHERE ba.bin = b.id AND ba.suite = su.id AND b.architecture = a.id AND b.file = f.id AND af.file_id = f.id AND af.archive_id = su.archive_id AND af.component_id = c.id %s %s %s %s" % (con_packages, con_suites, con_components, con_architectures)) to_remove.extend(q) else: # Source-only if not Options["Binary-Only"]: q = session.execute("SELECT s.source, s.version, 'source', s.id, s.maintainer FROM source s, src_associations sa, suite su, archive, files f, files_archive_map af, component c WHERE sa.source = s.id AND sa.suite = su.id AND archive.id = su.archive_id AND s.file = f.id AND af.file_id = f.id AND af.archive_id = su.archive_id AND af.component_id = c.id %s %s %s" % (con_packages, con_suites, con_components)) to_remove.extend(q) if not Options["Source-Only"]: # Source + Binary q = session.execute(""" SELECT b.package, b.version, a.arch_string, b.id, b.maintainer FROM binaries b JOIN bin_associations ba ON b.id = ba.bin JOIN architecture a ON b.architecture = a.id JOIN suite su ON ba.suite = su.id JOIN archive ON archive.id = su.archive_id JOIN files_archive_map af ON b.file = af.file_id AND af.archive_id = archive.id JOIN component c ON af.component_id = c.id JOIN source s ON b.source = s.id JOIN src_associations sa ON s.id = sa.source AND sa.suite = su.id WHERE TRUE %s %s %s %s""" % (con_packages, con_suites, con_components, con_architectures)) to_remove.extend(q) if not to_remove: print "Nothing to do." sys.exit(0) # If we don't have a reason; spawn an editor so the user can add one # Write the rejection email out as the <foo>.reason file if not Options["Reason"] and not Options["No-Action"]: (fd, temp_filename) = utils.temp_filename() editor = os.environ.get("EDITOR","vi") result = os.system("%s %s" % (editor, temp_filename)) if result != 0: utils.fubar ("vi invocation failed for `%s'!" % (temp_filename), result) temp_file = utils.open_file(temp_filename) for line in temp_file.readlines(): Options["Reason"] += line temp_file.close() os.unlink(temp_filename) # Generate the summary of what's to be removed d = {} for i in to_remove: package = i[0] version = i[1] architecture = i[2] maintainer = i[4] maintainers[maintainer] = "" if not d.has_key(package): d[package] = {} if not d[package].has_key(version): d[package][version] = [] if architecture not in d[package][version]: d[package][version].append(architecture) maintainer_list = [] for maintainer_id in maintainers.keys(): maintainer_list.append(get_maintainer(maintainer_id).name) summary = "" removals = d.keys() removals.sort() versions = [] for package in removals: versions = d[package].keys() versions.sort(apt_pkg.version_compare) for version in versions: d[package][version].sort(utils.arch_compare_sw) summary += "%10s | %10s | %s\n" % (package, version, ", ".join(d[package][version])) print "Will remove the following packages from %s:" % (suites_list) print print summary print "Maintainer: %s" % ", ".join(maintainer_list) if Options["Done"]: print "Will also close bugs: "+Options["Done"] if carbon_copy: print "Will also send CCs to: " + ", ".join(carbon_copy) if Options["Do-Close"]: print "Will also close associated bug reports." print print "------------------- Reason -------------------" print Options["Reason"] print "----------------------------------------------" print if Options["Rdep-Check"]: arches = utils.split_args(Options["Architecture"]) reverse_depends_check(removals, suites[0], arches, session) # If -n/--no-action, drop out here if Options["No-Action"]: sys.exit(0) print "Going to remove the packages now." game_over() whoami = utils.whoami() date = commands.getoutput('date -R') # Log first; if it all falls apart I want a record that we at least tried. logfile = utils.open_file(cnf["Rm::LogFile"], 'a') logfile.write("=========================================================================\n") logfile.write("[Date: %s] [ftpmaster: %s]\n" % (date, whoami)) logfile.write("Removed the following packages from %s:\n\n%s" % (suites_list, summary)) if Options["Done"]: logfile.write("Closed bugs: %s\n" % (Options["Done"])) logfile.write("\n------------------- Reason -------------------\n%s\n" % (Options["Reason"])) logfile.write("----------------------------------------------\n") # Do the same in rfc822 format logfile822 = utils.open_file(cnf["Rm::LogFile822"], 'a') logfile822.write("Date: %s\n" % date) logfile822.write("Ftpmaster: %s\n" % whoami) logfile822.write("Suite: %s\n" % suites_list) sources = [] binaries = [] for package in summary.split("\n"): for row in package.split("\n"): element = row.split("|") if len(element) == 3: if element[2].find("source") > 0: sources.append("%s_%s" % tuple(elem.strip(" ") for elem in element[:2])) element[2] = sub("source\s?,?", "", element[2]).strip(" ") if element[2]: binaries.append("%s_%s [%s]" % tuple(elem.strip(" ") for elem in element)) if sources: logfile822.write("Sources:\n") for source in sources: logfile822.write(" %s\n" % source) if binaries: logfile822.write("Binaries:\n") for binary in binaries: logfile822.write(" %s\n" % binary) logfile822.write("Reason: %s\n" % Options["Reason"].replace('\n', '\n ')) if Options["Done"]: logfile822.write("Bug: %s\n" % Options["Done"]) dsc_type_id = get_override_type('dsc', session).overridetype_id deb_type_id = get_override_type('deb', session).overridetype_id # Do the actual deletion print "Deleting...", sys.stdout.flush() for i in to_remove: package = i[0] architecture = i[2] package_id = i[3] for suite_id in suite_ids_list: if architecture == "source": session.execute("DELETE FROM src_associations WHERE source = :packageid AND suite = :suiteid", {'packageid': package_id, 'suiteid': suite_id}) #print "DELETE FROM src_associations WHERE source = %s AND suite = %s" % (package_id, suite_id) else: session.execute("DELETE FROM bin_associations WHERE bin = :packageid AND suite = :suiteid", {'packageid': package_id, 'suiteid': suite_id}) #print "DELETE FROM bin_associations WHERE bin = %s AND suite = %s" % (package_id, suite_id) # Delete from the override file if not Options["Partial"]: if architecture == "source": type_id = dsc_type_id else: type_id = deb_type_id # TODO: Again, fix this properly to remove the remaining non-bind argument session.execute("DELETE FROM override WHERE package = :package AND type = :typeid AND suite = :suiteid %s" % (over_con_components), {'package': package, 'typeid': type_id, 'suiteid': suite_id}) session.commit() print "done." # If we don't have a Bug server configured, we're done if not cnf.has_key("Dinstall::BugServer"): if Options["Done"] or Options["Do-Close"]: print "Cannot send mail to BugServer as Dinstall::BugServer is not configured" logfile.write("=========================================================================\n") logfile.close() logfile822.write("\n") logfile822.close() return # read common subst variables for all bug closure mails Subst_common = {} Subst_common["__RM_ADDRESS__"] = cnf["Dinstall::MyEmailAddress"] Subst_common["__BUG_SERVER__"] = cnf["Dinstall::BugServer"] Subst_common["__CC__"] = "X-DAK: dak rm" if carbon_copy: Subst_common["__CC__"] += "\nCc: " + ", ".join(carbon_copy) Subst_common["__SUITE_LIST__"] = suites_list Subst_common["__SUBJECT__"] = "Removed package(s) from %s" % (suites_list) Subst_common["__ADMIN_ADDRESS__"] = cnf["Dinstall::MyAdminAddress"] Subst_common["__DISTRO__"] = cnf["Dinstall::MyDistribution"] Subst_common["__WHOAMI__"] = whoami # Send the bug closing messages if Options["Done"]: Subst_close_rm = Subst_common bcc = [] if cnf.find("Dinstall::Bcc") != "": bcc.append(cnf["Dinstall::Bcc"]) if cnf.find("Rm::Bcc") != "": bcc.append(cnf["Rm::Bcc"]) if bcc: Subst_close_rm["__BCC__"] = "Bcc: " + ", ".join(bcc) else: Subst_close_rm["__BCC__"] = "X-Filler: 42" summarymail = "%s\n------------------- Reason -------------------\n%s\n" % (summary, Options["Reason"]) summarymail += "----------------------------------------------\n" Subst_close_rm["__SUMMARY__"] = summarymail for bug in utils.split_args(Options["Done"]): Subst_close_rm["__BUG_NUMBER__"] = bug if Options["Do-Close"]: mail_message = utils.TemplateSubst(Subst_close_rm,cnf["Dir::Templates"]+"/rm.bug-close-with-related") else: mail_message = utils.TemplateSubst(Subst_close_rm,cnf["Dir::Templates"]+"/rm.bug-close") utils.send_mail(mail_message, whitelists=whitelists) # close associated bug reports if Options["Do-Close"]: Subst_close_other = Subst_common bcc = [] wnpp = utils.parse_wnpp_bug_file() versions = list(set([re_bin_only_nmu.sub('', v) for v in versions])) if len(versions) == 1: Subst_close_other["__VERSION__"] = versions[0] else: utils.fubar("Closing bugs with multiple package versions is not supported. Do it yourself.") if bcc: Subst_close_other["__BCC__"] = "Bcc: " + ", ".join(bcc) else: Subst_close_other["__BCC__"] = "X-Filler: 42" # at this point, I just assume, that the first closed bug gives # some useful information on why the package got removed Subst_close_other["__BUG_NUMBER__"] = utils.split_args(Options["Done"])[0] if len(sources) == 1: source_pkg = source.split("_", 1)[0] else: utils.fubar("Closing bugs for multiple source packages is not supported. Do it yourself.") Subst_close_other["__BUG_NUMBER_ALSO__"] = "" Subst_close_other["__SOURCE__"] = source_pkg merged_bugs = set() other_bugs = bts.get_bugs('src', source_pkg, 'status', 'open', 'status', 'forwarded') if other_bugs: for bugno in other_bugs: if bugno not in merged_bugs: for bug in bts.get_status(bugno): for merged in bug.mergedwith: other_bugs.remove(merged) merged_bugs.add(merged) logfile.write("Also closing bug(s):") logfile822.write("Also-Bugs:") for bug in other_bugs: Subst_close_other["__BUG_NUMBER_ALSO__"] += str(bug) + "-done@" + cnf["Dinstall::BugServer"] + "," logfile.write(" " + str(bug)) logfile822.write(" " + str(bug)) logfile.write("\n") logfile822.write("\n") if source_pkg in wnpp.keys(): logfile.write("Also closing WNPP bug(s):") logfile822.write("Also-WNPP:") for bug in wnpp[source_pkg]: # the wnpp-rm file we parse also contains our removal # bugs, filtering that out if bug != Subst_close_other["__BUG_NUMBER__"]: Subst_close_other["__BUG_NUMBER_ALSO__"] += str(bug) + "-done@" + cnf["Dinstall::BugServer"] + "," logfile.write(" " + str(bug)) logfile822.write(" " + str(bug)) logfile.write("\n") logfile822.write("\n") mail_message = utils.TemplateSubst(Subst_close_other,cnf["Dir::Templates"]+"/rm.bug-close-related") if Subst_close_other["__BUG_NUMBER_ALSO__"]: utils.send_mail(mail_message) logfile.write("=========================================================================\n") logfile.close() logfile822.write("\n") logfile822.close()
def issues(self): # Initialise empty list of bug numbers collected_bugs = [] # Search BTS for bugs owned by email address if self.email: owned_bugs = debianbts.get_bugs("owner", self.email, "status", "open") collected_bugs.extend(owned_bugs) # Search BTS for bugs related to specified packages if self.packages: packages = self.packages.split(",") for pkg in packages: pkg_bugs = debianbts.get_bugs("package", pkg, "status", "open") for bug in pkg_bugs: if bug not in collected_bugs: collected_bugs.append(bug) # Search UDD bugs search for bugs belonging to packages that # are maintained by the email address if self.udd: udd_bugs = self._get_udd_bugs() for bug in udd_bugs: if bug not in collected_bugs: collected_bugs.append(bug['id']) issues = [self._record_for_bug(bug) for bug in debianbts.get_status(collected_bugs)] log.debug(" Found %i total.", len(issues)) if self.ignore_pkg: ignore_pkg = self.ignore_pkg.split(",") for pkg in ignore_pkg: issues = [issue for issue in issues if not issue['package'] == pkg] if self.ignore_src: ignore_src = self.ignore_src.split(",") for src in ignore_src: issues = [issue for issue in issues if not issue['source'] == src] if self.ignore_pending: issues = [issue for issue in issues if not issue['status'] == 'pending-fixed'] issues = [issue for issue in issues if not (issue['status'] == 'done' or issue['status'] == 'fixed')] log.debug(" Pruned down to %i.", len(issues)) for issue in issues: issue_obj = self.get_issue_for_record(issue) extra = { 'annotations': self.annotations(issue, issue_obj) } issue_obj.update_extra(extra) yield issue_obj
import_cmd += ' --upstream-tag=' + pkg_name + '/' + upstream_tag import_cmd += '/' + pkg_ver + ' --upstream-version=' + pkg_ver import_cmd += ' ' + import_tarball if verbose: print "Importing with this command:\n%s" % import_cmd if dry_run: print "DRY RUN: command not executed" else: if os.system(import_cmd) == 0: if verbose: print "Command executed successfully!" else: print "Command execution failed! Bailing out ..." sys.exit(1) # Pull bug reports, and remind user to check for closures try: bugs_fixed_upstream = debianbts.get_bugs('package', pkg_name, 'tag', 'fixed-upstream') bugs_upstream = debianbts.get_bugs('package', pkg_name, 'tag', 'upstream') bugs_all = debianbts.get_bugs('package', pkg_name) print "\n" + "**********" * 8 if len(bugs_fixed_upstream) > 0: print "\nBugs tagged 'fixed-upstream':" for b in bugs_fixed_upstream: if b in bugs_all: bugs_all.remove(b) # avoid repeats below s = debianbts.get_status(b) if not s[0].done: print "\tBug %s: %s" % (s[0].bug_num, s[0].subject) if len(bugs_upstream) > 0: print "\nBugs tagged 'upstream':" for b in bugs_upstream: if b in bugs_all: bugs_all.remove(b) # avoid repeats below s = debianbts.get_status(b)
action="store_true", help='dont generate images (for DEBUG)') parser.add_argument('--no-pypi', default=False, action="store_true", help='dont look for modules on PyPI (for DEBUG)') args = parser.parse_args() if not os.path.isdir(args.destdir): os.makedirs(args.destdir) log('Retrieving WNPP bugs information...') if args.bugs: wnpp_bugs_ids = args.bugs else: wnpp_bugs_ids = debianbts.get_bugs('package', 'wnpp') if args.limit: wnpp_bugs_ids = wnpp_bugs_ids[:args.limit] log(f"Found {len(wnpp_bugs_ids)} WNPP bugs, getting status...") wnpp_bugs = debianbts.get_status(wnpp_bugs_ids) wnpp = {} for wnpp_bug in wnpp_bugs: if wnpp_bug.done: continue m = WNPPRE.match(wnpp_bug.subject) if m: tag, src = m.groups() wnpp[src] = (tag, wnpp_bug.bug_num) else: log(f"Badly formatted WNPP bug: retitle {wnpp_bug.bug_num} \"{wnpp_bug.subject}\"" )
def lineedit_return_pressed(self): # # just in case ;) # text = unicode(self.lineEdit.text()) if text.startswith("http://"): self._show_url(text) return if len(text) == 0: return self.logger.info("Return pressed.") self.lineEdit.clear() # TODO: self.lineEdit.clear() does not always work, why? #QtCore.QTimer.singleShot(0,self.lineEdit,QtCore.SLOT("clear()")) query = rng.translate_query(text) self.logger.debug("Query: %s" % str(query)) # test if there is a submit-as field available and rename the packages # if nececesairy for i in range(0, len(query), 2): if query[i] == 'package': realname = bug.submit_as(query[i + 1]) if query[i + 1] != realname: self.logger.debug( "Using %s as package name as requested by developer." % str(realname)) query[i + 1] = realname # Single bug or list of bugs? if query[0]: buglist = bts.get_bugs(query) else: buglist = [query[1]] # ok, we know the package, so enable some buttons which don't depend # on the existence of the acutal packe (wnpp) or bugreports for that # package. if query[0] in ("src", "package"): self._stateChanged(query[1], None) # if we got a bugnumber we'd like to select it and enable some more # buttons. unfortunately we don't know if the bugnumber actually exists # for now, so we have to wait a bit until the bug is fetched. else: self._stateChanged(None, None) self.logger.debug("Buglist matching the query: %s" % str(buglist)) chunksize = 50 if len(buglist) > chunksize: self.load_started() self.logger.debug("Buglist longer than %i, splitting in chunks." % chunksize) self.bugs = [] i = 0 for chunk in chunks(buglist, chunksize): i += 1 progress = int(100. * i * chunksize / len(buglist)) if progress > 100: progress = 100 self.load_progress(progress) bl = bts.get_status(chunk) if len(bl) == 0: self.logger.error( "One of the following bugs caused the BTS to hickup: %s" % str(bl)) self.bugs.extend(bl) self.load_finished(True) else: self.bugs = bts.get_status(buglist) # ok, we fetched the bugs. see if the list isn't empty if query[0] in (None, ) and len(self.bugs) > 0: self.currentBug = self.bugs[0] self.currentPackage = self.currentBug.package self._stateChanged(self.currentPackage, self.currentBug) self.model.set_elements(self.bugs) self.tableView.resizeRowsToContents()
def get_package_bugs(source): import debianbts return set(debianbts.get_bugs(src=source, status="open"))