""" General purpose package removal tool for ftpmaster """
# Copyright (C) 2000, 2001, 2002, 2003, 2004, 2006 James Troup <james@nocrew.org>
+# Copyright (C) 2010 Alexander Reichle-Schmehl <tolimar@debian.org>
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
import commands
import os
-import re
import sys
import apt_pkg
import apt_inst
+from re import sub
from daklib.config import Config
from daklib.dbconn import *
from daklib import utils
from daklib.dak_exceptions import *
from daklib.regexes import re_strip_source_version, re_build_dep_arch
+import debianbts as bts
################################################################################
-c, --component=COMPONENT act on this component
-C, --carbon-copy=EMAIL send a CC of removal message to EMAIL
-d, --done=BUG# send removal message as closure to bug#
+ -D, --do-close also close all bugs associated to that package
-h, --help show this help and exit
-m, --reason=MSG reason for removal
-n, --no-action don't do anything
-S, --source-only remove source only
ARCH, BUG#, COMPONENT and SUITE can be comma (or space) separated lists, e.g.
- --architecture=m68k,i386"""
+ --architecture=amd64,i386"""
sys.exit(exit_code)
if arches:
all_arches = set(arches)
else:
- all_arches = set(get_suite_architectures(suites[0]))
+ all_arches = set([x.arch_string for x in get_suite_architectures(suites[0])])
all_arches -= set(["source", "all"])
for architecture in all_arches:
deps = {}
(result, output) = commands.getstatusoutput("gunzip -c %s > %s" % (filename, temp_filename))
if (result != 0):
utils.fubar("Gunzip invocation failed!\n%s\n" % (output), result)
+ # Also check for udebs
+ filename = "%s/dists/%s/%s/debian-installer/binary-%s/Packages.gz" % (cnf["Dir::Root"], suites[0], component, architecture)
+ if os.path.exists(filename):
+ (result, output) = commands.getstatusoutput("gunzip -c %s >> %s" % (filename, temp_filename))
+ if (result != 0):
+ utils.fubar("Gunzip invocation failed!\n%s\n" % (output), result)
packages = utils.open_file(temp_filename)
Packages = apt_pkg.ParseTagFile(packages)
while Packages.Step():
('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"),
for i in [ "architecture", "binary-only", "carbon-copy", "component",
"done", "help", "no-action", "partial", "rdep-check", "reason",
- "source-only" ]:
+ "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"):
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-Only"]:
+ utils.fubar("No.")
+ if Options["Do-Close"] and Options["Source-Only"]:
+ utils.fubar("No.")
+ if Options["Do-Close"] and Options["Suite"] != 'unstable':
+ 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
for i in source_packages.keys():
filename = "/".join(source_packages[i])
try:
- dsc = utils.parse_changes(filename)
+ dsc = utils.parse_changes(filename, dsc_file=1)
except CantOpenError:
utils.warn("couldn't open '%s'." % (filename))
continue
summary = ""
removals = d.keys()
removals.sort()
+ versions = []
for package in removals:
versions = d[package].keys()
versions.sort(apt_pkg.VersionCompare)
logfile.write("Closed bugs: %s\n" % (Options["Done"]))
logfile.write("\n------------------- Reason -------------------\n%s\n" % (Options["Reason"]))
logfile.write("----------------------------------------------\n")
- logfile.flush()
+ logfile.write("=========================================================================\n")
+ logfile.close()
+
+ # 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"])
+ logfile822.write("\n")
+ logfile822.close()
- dsc_type_id = get_override_type('dsc', session)
- deb_type_id = get_override_type('deb', session)
+ 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...",
session.commit()
print "done."
+ Subst = {}
+ Subst["__RM_ADDRESS__"] = cnf["Dinstall::MyEmailAddress"]
+ Subst["__BUG_SERVER__"] = cnf["Dinstall::BugServer"]
+ bcc = []
+ if cnf.Find("Dinstall::Bcc") != "":
+ bcc.append(cnf["Dinstall::Bcc"])
+ if cnf.Find("Rm::Bcc") != "":
+ bcc.append(cnf["Rm::Bcc"])
+ if bcc:
+ Subst["__BCC__"] = "Bcc: " + ", ".join(bcc)
+ else:
+ Subst["__BCC__"] = "X-Filler: 42"
+ Subst["__CC__"] = "X-DAK: dak rm"
+ if carbon_copy:
+ Subst["__CC__"] += "\nCc: " + ", ".join(carbon_copy)
+ Subst["__SUITE_LIST__"] = suites_list
+ Subst["__SUBJECT__"] = "Removed package(s) from %s" % (suites_list)
+ Subst["__ADMIN_ADDRESS__"] = cnf["Dinstall::MyAdminAddress"]
+ Subst["__DISTRO__"] = cnf["Dinstall::MyDistribution"]
+ Subst["__WHOAMI__"] = whoami
+
# Send the bug closing messages
if Options["Done"]:
- Subst = {}
- Subst["__RM_ADDRESS__"] = cnf["Rm::MyEmailAddress"]
- Subst["__BUG_SERVER__"] = cnf["Dinstall::BugServer"]
- bcc = []
- if cnf.Find("Dinstall::Bcc") != "":
- bcc.append(cnf["Dinstall::Bcc"])
- if cnf.Find("Rm::Bcc") != "":
- bcc.append(cnf["Rm::Bcc"])
- if bcc:
- Subst["__BCC__"] = "Bcc: " + ", ".join(bcc)
- else:
- Subst["__BCC__"] = "X-Filler: 42"
- Subst["__CC__"] = "X-DAK: dak rm\nX-Katie: melanie"
- if carbon_copy:
- Subst["__CC__"] += "\nCc: " + ", ".join(carbon_copy)
- Subst["__SUITE_LIST__"] = suites_list
- Subst["__SUMMARY__"] = summary
- Subst["__ADMIN_ADDRESS__"] = cnf["Dinstall::MyAdminAddress"]
- Subst["__DISTRO__"] = cnf["Dinstall::MyDistribution"]
- Subst["__WHOAMI__"] = whoami
+ summarymail = "%s\n------------------- Reason -------------------\n%s\n" % (summary, Options["Reason"])
+ summarymail += "----------------------------------------------\n"
+ Subst["__SUMMARY__"] = summarymail
whereami = utils.where_am_i()
Archive = cnf.SubTree("Archive::%s" % (whereami))
Subst["__MASTER_ARCHIVE__"] = Archive["OriginServer"]
mail_message = utils.TemplateSubst(Subst,cnf["Dir::Templates"]+"/rm.bug-close")
utils.send_mail(mail_message)
- logfile.write("=========================================================================\n")
- logfile.close()
+ # close associated bug reports
+ # FIXME: We should also close possible WNPP bugs for that package, but
+ # currently there's no sane way to determine them
+ if Options["Do-Close"]:
+ if len(versions) == 1:
+ Subst["__VERSION__"] = versions[0]
+ else:
+ utils.fubar("Closing bugs with multiple package versions is not supported. Do it yourself.")
+ whereami = utils.where_am_i()
+ Archive = cnf.SubTree("Archive::%s" % (whereami))
+ # at this point, I just assume, that the first closed bug gives
+ # some usefull information on why the package got removed
+ Subst["__BUG_NUMBER__"] = utils.split_args(Options["Done"])[0]
+ if len(sources) > 1:
+ utils.fubar("Closing bugs for multiple source pakcages is not supported. Do it yourself.")
+ Subst["__BUG_NUMBER_ALSO__"] = ""
+ Subst["__SOURCE__"] = source.split("_", 1)[0]
+ for bug in bts.get_bugs('src', source.split("_", 1)[0], 'status', 'open'):
+ Subst["__BUG_NUMBER_ALSO__"] += str(bug) + "-done@" + cnf["Dinstall::BugServer"] + ","
+ mail_message = utils.TemplateSubst(Subst,cnf["Dir::Templates"]+"/rm.bug-close-related")
+ if Subst["__BUG_NUMBER_ALSO__"]:
+ utils.send_mail(mail_message)
#######################################################################################