Merge branch 'p-u'

debian
Adam D. Barratt 13 years ago
commit 6de2f0ad22

@ -186,12 +186,13 @@ import sys
import string import string
import time import time
import optparse import optparse
import operator
import urllib import urllib
import apt_pkg import apt_pkg
from excuse import Excuse from excuse import Excuse
from migrationitem import MigrationItem, HintItem
from hints import HintCollection
from britney import buildSystem from britney import buildSystem
__author__ = 'Fabio Tranchitella and the Debian Release Team' __author__ = 'Fabio Tranchitella and the Debian Release Team'
@ -242,6 +243,7 @@ class Britney:
# parse the command line arguments # parse the command line arguments
self.__parse_arguments() self.__parse_arguments()
MigrationItem.set_architectures(self.options.architectures)
# initialize the apt_pkg back-end # initialize the apt_pkg back-end
apt_pkg.init() apt_pkg.init()
@ -769,7 +771,7 @@ class Britney:
The method returns a dictionary where the key is the command, and The method returns a dictionary where the key is the command, and
the value is the list of affected packages. the value is the list of affected packages.
""" """
hints = dict([(k,[]) for k in self.HINTS_ALL]) hints = HintCollection()
for who in self.HINTS.keys(): for who in self.HINTS.keys():
if who == 'command-line': if who == 'command-line':
@ -789,35 +791,40 @@ class Britney:
break break
elif l[0] not in self.HINTS[who]: elif l[0] not in self.HINTS[who]:
continue continue
elif l[0] in ["easy", "hint", "force-hint"]: elif l[0] in ["approve", "block", "block-all", "block-udeb", "unblock", "unblock-udeb", "force", "urgent", "remove"]:
hints[l[0]].append((who, [k.rsplit("/", 1) for k in l if "/" in k])) for package in l[1:]:
elif l[0] in ["block-all"]: hints.add_hint('%s %s' % (l[0], package), who)
hints[l[0]].extend([(y, who) for y in l[1:]]) elif l[0] in ["age-days"]:
elif l[0] in ["block", "block-udeb"]: for package in l[2:]:
hints[l[0]].extend([(y, who) for y in l[1:]]) hints.add_hint('%s %s %s' % (l[0], l[1], package), who)
elif l[0] in ["age-days"] and len(l) >= 3 and l[1].isdigit(): else:
days = l[1] hints.add_hint(l, who)
tmp = [tuple([who] + k.rsplit("/", 1)) for k in l[2:] if "/" in k]
hints[l[0]].extend([(p, (v, h, days)) for h, p, v in tmp])
elif l[0] in ["remove", "approve", "unblock", "unblock-udeb", "force", "urgent"]:
hints[l[0]].extend([(k.rsplit("/", 1)[0], (k.rsplit("/", 1)[1], who)) for k in l if "/" in k])
for x in ["approve", "block", "block-all", "block-udeb", "unblock", "unblock-udeb", "force", "urgent", "remove", "age-days"]: for x in ["approve", "block", "block-all", "block-udeb", "unblock", "unblock-udeb", "force", "urgent", "remove", "age-days"]:
z = {} z = {}
for a, b in hints[x]: for hint in hints[x]:
if z.has_key(a) and z[a] != b: package = hint.package
key = (hint, hint.user)
if z.has_key(package) and z[package] != key:
hint2 = z[package][0]
if x in ['unblock', 'unblock-udeb']: if x in ['unblock', 'unblock-udeb']:
if apt_pkg.VersionCompare(z[a][0], b[0]) < 0: if apt_pkg.VersionCompare(hint2.version, hint.version) < 0:
# This hint is for a newer version, so discard the old one # This hint is for a newer version, so discard the old one
self.__log("Overriding %s[%s] = %s with %s" % (x, a, z[a], b), type="W") self.__log("Overriding %s[%s] = ('%s', '%s') with ('%s', '%s')" %
(x, package, hint2.version, hint2.user, hint.version, hint.user), type="W")
hint2.set_active(False)
else: else:
# This hint is for an older version, so ignore it in favour of the new one # This hint is for an older version, so ignore it in favour of the new one
self.__log("Ignoring %s[%s] = %s, %s is higher or equal" % (x, a, b, z[a]), type="W") self.__log("Ignoring %s[%s] = ('%s', '%s'), ('%s', '%s') is higher or equal" %
continue (x, package, hint.version, hint.user, hint2.version, hint2.user), type="W")
hint.set_active(False)
else: else:
self.__log("Overriding %s[%s] = %s with %s" % (x, a, z[a], b), type="W") self.__log("Overriding %s[%s] = ('%s', '%s', '%s') with ('%s', '%s', '%s')" %
z[a] = b (x, package, hint2.version, hint2.user, hint2.days,
hints[x] = z hint.version, hint.user, hint.days), type="W")
hint2.set_active(False)
z[package] = key
# Sanity check the hints hash # Sanity check the hints hash
if len(hints["block"]) == 0 and len(hints["block-udeb"]) == 0: if len(hints["block"]) == 0 and len(hints["block-udeb"]) == 0:
@ -825,7 +832,9 @@ class Britney:
# A (t-)p-u approval overrides an unstable block # A (t-)p-u approval overrides an unstable block
for p in hints["approve"]: for p in hints["approve"]:
hints["unblock"][p] = hints["approve"][p] for o in hints.search('unblock', package=p.package):
o.set_active(False)
hints.add_hint('unblock %s/%s' % (p.package, p.version), p.user)
return hints return hints
@ -1078,9 +1087,9 @@ class Britney:
src[SECTION] and excuse.set_section(src[SECTION].strip()) src[SECTION] and excuse.set_section(src[SECTION].strip())
# if the package is blocked, skip it # if the package is blocked, skip it
if self.hints['block'].has_key('-' + pkg): for hint in self.hints.search('block', package=pkg, removal=True):
excuse.addhtml("Not touching package, as requested by %s (contact debian-release " excuse.addhtml("Not touching package, as requested by %s (contact debian-release "
"if update is needed)" % self.hints['block']['-' + pkg]) "if update is needed)" % hint.user)
excuse.addhtml("Not considered") excuse.addhtml("Not considered")
self.excuses.append(excuse) self.excuses.append(excuse)
return False return False
@ -1115,9 +1124,8 @@ class Britney:
# if there is a `remove' hint and the requested version is the same as the # if there is a `remove' hint and the requested version is the same as the
# version in testing, then stop here and return False # version in testing, then stop here and return False
if src in self.hints["remove"] and \ for hint in [ x for x in self.hints.search('remove', package=src) if self.same_source(source_t[VERSION], x.version) ]:
self.same_source(source_t[VERSION], self.hints["remove"][src][0]): excuse.addhtml("Removal request by %s" % (hint.user))
excuse.addhtml("Removal request by %s" % (self.hints["remove"][src][1]))
excuse.addhtml("Trying to remove package, not update it") excuse.addhtml("Trying to remove package, not update it")
excuse.addhtml("Not considered") excuse.addhtml("Not considered")
self.excuses.append(excuse) self.excuses.append(excuse)
@ -1254,38 +1262,37 @@ class Britney:
# if there is a `remove' hint and the requested version is the same as the # if there is a `remove' hint and the requested version is the same as the
# version in testing, then stop here and return False # version in testing, then stop here and return False
if src in self.hints["remove"]: for item in self.hints.search('remove', package=src):
if source_t and self.same_source(source_t[VERSION], self.hints['remove'][src][0]) or \ if source_t and self.same_source(source_t[VERSION], item.version) or \
self.same_source(source_u[VERSION], self.hints['remove'][src][0]): self.same_source(source_u[VERSION], item.version):
excuse.addhtml("Removal request by %s" % (self.hints["remove"][src][1])) excuse.addhtml("Removal request by %s" % (item.user))
excuse.addhtml("Trying to remove package, not update it") excuse.addhtml("Trying to remove package, not update it")
update_candidate = False update_candidate = False
# check if there is a `block' or `block-udeb' hint for this package, or a `block-all source' hint # check if there is a `block' or `block-udeb' hint for this package, or a `block-all source' hint
blocked = {} blocked = {}
if src in self.hints["block"]: for hint in self.hints.search(package=src):
blocked["block"] = self.hints["block"][src] if hint.type == 'block' or (hint.type == 'block-all' and hint.package == 'source' and hint not in blocked['block']):
elif 'source' in self.hints["block-all"]: blocked['block'] = hint
blocked["block"] = self.hints["block-all"]["source"] if hint.type == 'block-udeb':
blocked['block-udeb'] = hint
if src in self.hints["block-udeb"]:
blocked["block-udeb"] = self.hints["block-udeb"][src]
# if the source is blocked, then look for an `unblock' hint; the unblock request # if the source is blocked, then look for an `unblock' hint; the unblock request
# is processed only if the specified version is correct. If a package is blocked # is processed only if the specified version is correct. If a package is blocked
# by `block-udeb', then `unblock-udeb' must be present to cancel it. # by `block-udeb', then `unblock-udeb' must be present to cancel it.
for block_cmd in blocked: for block_cmd in blocked:
unblock_cmd = "un" + block_cmd unblock_cmd = "un" + block_cmd
unblock = self.hints[unblock_cmd].get(src,(None,None)) unblocks = self.hints.search(unblock_cmd, package=src)
if unblock[0] != None and self.same_source(unblock[0], source_u[VERSION]):
if unblocks and self.same_source(unblocks[0].version, source_u[VERSION]):
excuse.addhtml("Ignoring %s request by %s, due to %s request by %s" % excuse.addhtml("Ignoring %s request by %s, due to %s request by %s" %
(block_cmd, blocked[block_cmd], unblock_cmd, self.hints[unblock_cmd][src][1])) (block_cmd, blocked[block_cmd].user, unblock_cmd, unblocks[0].user))
else: else:
if unblock[0] != None: if unblocks:
excuse.addhtml("%s request by %s ignored due to version mismatch: %s" % excuse.addhtml("%s request by %s ignored due to version mismatch: %s" %
(unblock_cmd.capitalize(), self.hints[unblock_cmd][src][1], self.hints[unblock_cmd][src][0])) (unblock_cmd.capitalize(), unblocks[0].user, unblocks[0].version))
excuse.addhtml("Not touching package due to %s request by %s (contact debian-release if update is needed)" % excuse.addhtml("Not touching package due to %s request by %s (contact debian-release if update is needed)" %
(block_cmd, blocked[block_cmd])) (block_cmd, blocked[block_cmd].user))
update_candidate = False update_candidate = False
# if the suite is unstable, then we have to check the urgency and the minimum days of # if the suite is unstable, then we have to check the urgency and the minimum days of
@ -1301,17 +1308,18 @@ class Britney:
days_old = self.date_now - self.dates[src][1] days_old = self.date_now - self.dates[src][1]
min_days = self.MINDAYS[urgency] min_days = self.MINDAYS[urgency]
age_days_hint = self.hints["age-days"].get(src) for age_days_hint in [ x for x in self.hints.search('age-days', package=src) if \
if age_days_hint is not None and (age_days_hint[0] == "-" or \ self.same_source(source_u[VERSION], x.version) ]:
self.same_source(source_u[VERSION], age_days_hint[0])):
excuse.addhtml("Overriding age needed from %d days to %d by %s" % (min_days, excuse.addhtml("Overriding age needed from %d days to %d by %s" % (min_days,
int(self.hints["age-days"][src][2]), self.hints["age-days"][src][1])) int(age_days_hint.days), age_days_hint.user))
min_days = int(self.hints["age-days"][src][2]) min_days = int(age_days_hint.days)
excuse.setdaysold(days_old, min_days) excuse.setdaysold(days_old, min_days)
if days_old < min_days: if days_old < min_days:
if src in self.hints["urgent"] and self.same_source(source_u[VERSION], self.hints["urgent"][src][0]): urgent_hints = [ x for x in self.hints.search('urgent', package=src) if \
excuse.addhtml("Too young, but urgency pushed by %s" % (self.hints["urgent"][src][1])) self.same_source(source_u[VERSION], x.version) ]
if urgent_hints:
excuse.addhtml("Too young, but urgency pushed by %s" % (urgent_hints[0].user))
else: else:
update_candidate = False update_candidate = False
@ -1432,18 +1440,18 @@ class Britney:
"though it fixes more than it introduces, whine at debian-release)" % pkg) "though it fixes more than it introduces, whine at debian-release)" % pkg)
# check if there is a `force' hint for this package, which allows it to go in even if it is not updateable # check if there is a `force' hint for this package, which allows it to go in even if it is not updateable
if src in self.hints["force"] and self.same_source(source_u[VERSION], self.hints["force"][src][0]): forces = [ x for x in self.hints.search('force', package=src) if self.same_source(source_u[VERSION], x.version) ]
if forces:
excuse.dontinvalidate = 1 excuse.dontinvalidate = 1
if not update_candidate and src in self.hints["force"] and \ if not update_candidate and forces:
self.same_source(source_u[VERSION], self.hints["force"][src][0]): excuse.addhtml("Should ignore, but forced by %s" % (forces[0].user))
excuse.addhtml("Should ignore, but forced by %s" % (self.hints["force"][src][1]))
update_candidate = True update_candidate = True
# if the suite is *-proposed-updates, the package needs an explicit approval in order to go in # if the suite is *-proposed-updates, the package needs an explicit approval in order to go in
if suite in ['tpu', 'pu']: if suite in ['tpu', 'pu']:
if src in self.hints["approve"] and \ approves = [ x for x in self.hints.search('approve', package=src) if self.same_source(source_u[VERSION], x.version) ]
self.same_source(source_u[VERSION], self.hints["approve"][src][0]): if approves:
excuse.addhtml("Approved by %s" % self.hints["approve"][src][1]) excuse.addhtml("Approved by %s" % approves[0].user)
else: else:
excuse.addhtml("NEEDS APPROVAL BY RM") excuse.addhtml("NEEDS APPROVAL BY RM")
update_candidate = False update_candidate = False
@ -1568,20 +1576,21 @@ class Britney:
upgrade_me.append("%s_%s" % (pkg, suite)) upgrade_me.append("%s_%s" % (pkg, suite))
# process the `remove' hints, if the given package is not yet in upgrade_me # process the `remove' hints, if the given package is not yet in upgrade_me
for src in self.hints["remove"].keys(): for item in self.hints['remove']:
src = item.package
if src in upgrade_me: continue if src in upgrade_me: continue
if ("-"+src) in upgrade_me: continue if ("-"+src) in upgrade_me: continue
if src not in sources['testing']: continue if src not in sources['testing']: continue
# check if the version specified in the hint is the same as the considered package # check if the version specified in the hint is the same as the considered package
tsrcv = sources['testing'][src][VERSION] tsrcv = sources['testing'][src][VERSION]
if not self.same_source(tsrcv, self.hints["remove"][src][0]): continue if not self.same_source(tsrcv, item.version): continue
# add the removal of the package to upgrade_me and build a new excuse # add the removal of the package to upgrade_me and build a new excuse
upgrade_me.append("-%s" % (src)) upgrade_me.append("-%s" % (src))
excuse = Excuse("-%s" % (src)) excuse = Excuse("-%s" % (src))
excuse.set_vers(tsrcv, None) excuse.set_vers(tsrcv, None)
excuse.addhtml("Removal request by %s" % (self.hints["remove"][src][1])) excuse.addhtml("Removal request by %s" % (item.user))
excuse.addhtml("Package is broken, will try to remove") excuse.addhtml("Package is broken, will try to remove")
self.excuses.append(excuse) self.excuses.append(excuse)
@ -1626,7 +1635,7 @@ class Britney:
self.invalidate_excuses(upgrade_me, unconsidered) self.invalidate_excuses(upgrade_me, unconsidered)
# sort the list of candidates # sort the list of candidates
self.upgrade_me = sorted(upgrade_me) self.upgrade_me = sorted([ MigrationItem(x) for x in upgrade_me ])
# write excuses to the output file # write excuses to the output file
if not self.options.dry_run: if not self.options.dry_run:
@ -1764,13 +1773,13 @@ class Britney:
return diff <= 0 return diff <= 0
def doop_source(self, pkg, hint_undo=[]): def doop_source(self, item, hint_undo=[]):
"""Apply a change to the testing distribution as requested by `pkg` """Apply a change to the testing distribution as requested by `pkg`
An optional list of undo actions related to packages processed earlier An optional list of undo actions related to packages processed earlier
in a hint may be passed in `hint_undo`. in a hint may be passed in `hint_undo`.
This method applies the changes required by the action `pkg` tracking This method applies the changes required by the action `item` tracking
them so it will be possible to revert them. them so it will be possible to revert them.
The method returns a list of the package name, the suite where the The method returns a list of the package name, the suite where the
@ -1780,41 +1789,14 @@ class Britney:
undo = {'binaries': {}, 'sources': {}, 'virtual': {}, 'nvirtual': []} undo = {'binaries': {}, 'sources': {}, 'virtual': {}, 'nvirtual': []}
affected = [] affected = []
arch = None
# local copies for better performances # local copies for better performances
sources = self.sources sources = self.sources
binaries = self.binaries['testing'] binaries = self.binaries['testing']
# removal of single-arch binary package = "-<package>/<arch>"
# only used for cleaning up after smooth-updates
if pkg[0] == "-" and "/" in pkg:
pkg_name, arch = pkg.split("/")
pkg_name = pkg_name[1:]
suite = "testing"
# arch = "<source>/<arch>",
elif "/" in pkg:
pkg_name, arch = pkg.split("/")
if arch.endswith("_tpu") or arch.endswith("_pu"):
arch, suite = arch.split("_")
else: suite = "unstable"
# removal of source packages = "-<source>",
elif pkg[0] == "-":
pkg_name = pkg[1:]
suite = "testing"
# testing-proposed-updates = "<source>_tpu"
# proposed-updates = "<source>_pu"
elif pkg.endswith("_tpu") or pkg.endswith("_pu"):
pkg_name, suite = pkg.rsplit("_")
# normal update of source packages = "<source>"
else:
pkg_name = pkg
suite = "unstable"
# remove all binary packages (if the source already exists) # remove all binary packages (if the source already exists)
if not (arch and pkg[0] == '-'): if item.architecture == 'source' or not item.is_removal:
if pkg_name in sources['testing']: if item.package in sources['testing']:
source = sources['testing'][pkg_name] source = sources['testing'][item.package]
bins = [] bins = []
check = [] check = []
@ -1825,16 +1807,16 @@ class Britney:
# first, build a list of eligible binaries # first, build a list of eligible binaries
for p in source[BINARIES]: for p in source[BINARIES]:
binary, parch = p.split("/") binary, parch = p.split("/")
if arch and parch != arch: continue if item.architecture != 'source' and parch != item.architecture: continue
# do not remove binaries which have been hijacked by other sources # do not remove binaries which have been hijacked by other sources
if binaries[parch][0][binary][SOURCE] != pkg_name: continue if binaries[parch][0][binary][SOURCE] != item.package: continue
bins.append(p) bins.append(p)
for p in bins: for p in bins:
binary, parch = p.split("/") binary, parch = p.split("/")
# if a smooth update is possible for the package, skip it # if a smooth update is possible for the package, skip it
if not self.options.compatible and suite == 'unstable' and \ if not self.options.compatible and item.suite == 'unstable' and \
binary not in self.binaries[suite][parch][0] and \ binary not in self.binaries[item.suite][parch][0] and \
('ALL' in self.options.smooth_updates or \ ('ALL' in self.options.smooth_updates or \
binaries[parch][0][binary][SECTION] in self.options.smooth_updates): binaries[parch][0][binary][SECTION] in self.options.smooth_updates):
@ -1881,29 +1863,29 @@ class Britney:
del binaries[parch][0][binary] del binaries[parch][0][binary]
self.systems[parch].remove_binary(binary) self.systems[parch].remove_binary(binary)
# remove the source package # remove the source package
if not arch: if item.architecture == 'source':
undo['sources'][pkg_name] = source undo['sources'][item.package] = source
del sources['testing'][pkg_name] del sources['testing'][item.package]
else: else:
# the package didn't exist, so we mark it as to-be-removed in case of undo # the package didn't exist, so we mark it as to-be-removed in case of undo
undo['sources']['-' + pkg_name] = True undo['sources']['-' + item.package] = True
# single binary removal; used for clearing up after smooth # single binary removal; used for clearing up after smooth
# updates but not supported as a manual hint # updates but not supported as a manual hint
elif pkg_name in binaries[arch][0]: elif item.package in binaries[item.architecture][0]:
undo['binaries'][pkg_name + "/" + arch] = binaries[arch][0][pkg_name] undo['binaries'][item.package + "/" + item.architecture] = binaries[item.architecture][0][item.package]
affected.extend( [ (x, arch) for x in \ affected.extend( [ (x, item.architecture) for x in \
self.get_reverse_tree(pkg_name, arch, 'testing') ] ) self.get_reverse_tree(item.package, item.architecture, 'testing') ] )
affected = list(set(affected)) affected = list(set(affected))
del binaries[arch][0][pkg_name] del binaries[item.architecture][0][item.package]
self.systems[arch].remove_binary(pkg_name) self.systems[item.architecture].remove_binary(item.package)
# add the new binary packages (if we are not removing) # add the new binary packages (if we are not removing)
if pkg[0] != "-": if not item.is_removal:
source = sources[suite][pkg_name] source = sources[item.suite][item.package]
for p in source[BINARIES]: for p in source[BINARIES]:
binary, parch = p.split("/") binary, parch = p.split("/")
if arch and parch != arch: continue if item.architecture not in ['source', parch]: continue
key = (binary, parch) key = (binary, parch)
# obviously, added/modified packages are affected # obviously, added/modified packages are affected
if key not in affected: affected.append(key) if key not in affected: affected.append(key)
@ -1931,7 +1913,7 @@ class Britney:
# ignored as their reverse trees are already handled # ignored as their reverse trees are already handled
# by this function # by this function
# XXX: and the reverse conflict tree? # XXX: and the reverse conflict tree?
for (tundo, tpkg, tpkg_name, tsuite) in hint_undo: for (tundo, tpkg) in hint_undo:
if p in tundo['binaries']: if p in tundo['binaries']:
for rdep in tundo['binaries'][p][RDEPENDS]: for rdep in tundo['binaries'][p][RDEPENDS]:
if rdep in binaries[parch][0] and rdep not in source[BINARIES]: if rdep in binaries[parch][0] and rdep not in source[BINARIES]:
@ -1940,7 +1922,7 @@ class Britney:
self.get_reverse_tree(rdep, parch, 'testing') ] ) self.get_reverse_tree(rdep, parch, 'testing') ] )
affected = list(set(affected)) affected = list(set(affected))
# add/update the binary package # add/update the binary package
binaries[parch][0][binary] = self.binaries[suite][parch][0][binary] binaries[parch][0][binary] = self.binaries[item.suite][parch][0][binary]
self.systems[parch].add_binary(binary, binaries[parch][0][binary][:PROVIDES] + \ self.systems[parch].add_binary(binary, binaries[parch][0][binary][:PROVIDES] + \
[", ".join(binaries[parch][0][binary][PROVIDES]) or None]) [", ".join(binaries[parch][0][binary][PROVIDES]) or None])
# register new provided packages # register new provided packages
@ -1960,15 +1942,15 @@ class Britney:
# register reverse dependencies and conflicts for the new binary packages # register reverse dependencies and conflicts for the new binary packages
for p in source[BINARIES]: for p in source[BINARIES]:
binary, parch = p.split("/") binary, parch = p.split("/")
if arch and parch != arch: continue if item.architecture not in ['source', parch]: continue
self.register_reverses(binary, binaries[parch][0] , binaries[parch][1]) self.register_reverses(binary, binaries[parch][0] , binaries[parch][1])
# add/update the source package # add/update the source package
if not arch: if item.architecture == 'source':
sources['testing'][pkg_name] = sources[suite][pkg_name] sources['testing'][item.package] = sources[item.suite][item.package]
# return the package name, the suite, the list of affected packages and the undo dictionary # return the package name, the suite, the list of affected packages and the undo dictionary
return (pkg_name, suite, affected, undo) return (item, affected, undo)
def get_reverse_tree(self, pkg, arch, suite): def get_reverse_tree(self, pkg, arch, suite):
packages = [] packages = []
@ -2045,9 +2027,9 @@ class Britney:
# pre-process a hint batch # pre-process a hint batch
pre_process = {} pre_process = {}
if selected and hint: if selected and hint:
for pkg in selected: for package in selected:
pkg_name, suite, affected, undo = self.doop_source(pkg) pkg, affected, undo = self.doop_source(package)
pre_process[pkg] = (pkg_name, suite, affected, undo) pre_process[package] = (pkg, affected, undo)
lundo = [] lundo = []
if not hint: if not hint:
@ -2083,14 +2065,14 @@ class Britney:
# apply the changes # apply the changes
if pkg in pre_process: if pkg in pre_process:
pkg_name, suite, affected, undo = pre_process[pkg] item, affected, undo = pre_process[pkg]
else: else:
pkg_name, suite, affected, undo = self.doop_source(pkg, lundo) item, affected, undo = self.doop_source(pkg, lundo)
if hint: if hint:
lundo.append((undo, pkg, pkg_name, suite)) lundo.append((undo, item))
# check the affected packages on all the architectures # check the affected packages on all the architectures
for arch in ("/" in pkg and (pkg.split("/")[1].split("_")[0],) or architectures): for arch in (item.architecture == 'source' and architectures or (item.architecture,)):
if arch not in nobreakall_arches: if arch not in nobreakall_arches:
skip_archall = True skip_archall = True
else: skip_archall = False else: skip_archall = False
@ -2159,17 +2141,17 @@ class Britney:
continue continue
# if the uninstallability counter is worse than before, break the loop # if the uninstallability counter is worse than before, break the loop
if (("/" in pkg and arch not in new_arches) or \ if ((item.architecture != 'source' and arch not in new_arches) or \
(arch not in break_arches)) and len(nuninst[arch]) > len(nuninst_comp[arch]): (arch not in break_arches)) and len(nuninst[arch]) > len(nuninst_comp[arch]):
better = False better = False
break break
# if we are processing hints or the package is already accepted, go ahead # if we are processing hints or the package is already accepted, go ahead
if hint or pkg in selected: continue if hint or item in selected: continue
# check if the action improved the uninstallability counters # check if the action improved the uninstallability counters
if better: if better:
lundo.append((undo, pkg, pkg_name, suite)) lundo.append((undo, item))
selected.append(pkg) selected.append(pkg)
packages.extend(extra) packages.extend(extra)
extra = [] extra = []
@ -2178,14 +2160,14 @@ class Britney:
self.output_write(" pre: %s\n" % (self.eval_nuninst(nuninst_comp))) self.output_write(" pre: %s\n" % (self.eval_nuninst(nuninst_comp)))
self.output_write(" now: %s\n" % (self.eval_nuninst(nuninst, nuninst_comp))) self.output_write(" now: %s\n" % (self.eval_nuninst(nuninst, nuninst_comp)))
if len(selected) <= 20: if len(selected) <= 20:
self.output_write(" all: %s\n" % (" ".join(selected))) self.output_write(" all: %s\n" % (" ".join([ str(x) for x in selected ])))
else: else:
self.output_write(" most: (%d) .. %s\n" % (len(selected), " ".join(selected[-20:]))) self.output_write(" most: (%d) .. %s\n" % (len(selected), " ".join([str(x) for x in selected][-20:])))
for k in nuninst: for k in nuninst:
nuninst_comp[k] = nuninst[k] nuninst_comp[k] = nuninst[k]
else: else:
self.output_write("skipped: %s (%d <- %d)\n" % (pkg, len(extra), len(packages))) self.output_write("skipped: %s (%d <- %d)\n" % (pkg, len(extra), len(packages)))
self.output_write(" got: %s\n" % (self.eval_nuninst(nuninst, "/" in pkg and nuninst_comp or None))) self.output_write(" got: %s\n" % (self.eval_nuninst(nuninst, pkg.architecture != 'source' and nuninst_comp or None)))
self.output_write(" * %s: %s\n" % (arch, ", ".join(sorted([b for b in nuninst[arch] if b not in nuninst_comp[arch]])))) self.output_write(" * %s: %s\n" % (arch, ", ".join(sorted([b for b in nuninst[arch] if b not in nuninst_comp[arch]]))))
extra.append(pkg) extra.append(pkg)
@ -2199,10 +2181,10 @@ class Britney:
else: sources['testing'][k] = undo['sources'][k] else: sources['testing'][k] = undo['sources'][k]
# undo the changes (new binaries) # undo the changes (new binaries)
if pkg[0] != '-' and pkg_name in sources[suite]: if not item.is_removal and item.package in sources[item.suite]:
for p in sources[suite][pkg_name][BINARIES]: for p in sources[item.suite][item.package][BINARIES]:
binary, arch = p.split("/") binary, arch = p.split("/")
if '/' not in pkg or pkg.endswith("/%s" % (arch)) or pkg.endswith("/%s_tpu" % (arch)) or pkg.endswith("/%s_pu" % (arch)): if item.architecture in ['source', arch]:
del binaries[arch][0][binary] del binaries[arch][0][binary]
self.systems[arch].remove_binary(binary) self.systems[arch].remove_binary(binary)
@ -2232,7 +2214,7 @@ class Britney:
if hint: if hint:
return (nuninst_comp, [], lundo) return (nuninst_comp, [], lundo)
self.output_write(" finish: [%s]\n" % ",".join(selected)) self.output_write(" finish: [%s]\n" % ",".join([ str(x) for x in selected ]))
self.output_write("endloop: %s\n" % (self.eval_nuninst(self.nuninst_orig))) self.output_write("endloop: %s\n" % (self.eval_nuninst(self.nuninst_orig)))
self.output_write(" now: %s\n" % (self.eval_nuninst(nuninst_comp))) self.output_write(" now: %s\n" % (self.eval_nuninst(nuninst_comp)))
self.output_write(self.eval_uninst(self.newlyuninst(self.nuninst_orig, nuninst_comp))) self.output_write(self.eval_uninst(self.newlyuninst(self.nuninst_orig, nuninst_comp)))
@ -2265,10 +2247,10 @@ class Britney:
# if we have a list of initial packages, check them # if we have a list of initial packages, check them
if init: if init:
self.output_write("leading: %s\n" % (",".join(init))) self.output_write("leading: %s\n" % (",".join([ str(x) for x in init ])))
for x in init: for x in init:
if x not in upgrade_me: if x not in upgrade_me:
self.output_write("failed: %s\n" % (x)) self.output_write("failed: %s\n" % (x.uvname))
return None return None
selected.append(x) selected.append(x)
upgrade_me.remove(x) upgrade_me.remove(x)
@ -2303,7 +2285,7 @@ class Britney:
if nuninst_end: if nuninst_end:
if not force and not earlyabort: if not force and not earlyabort:
self.output_write("Apparently successful\n") self.output_write("Apparently successful\n")
self.output_write("final: %s\n" % ",".join(sorted(selected))) self.output_write("final: %s\n" % ",".join(sorted([ str(x) for x in selected ])))
self.output_write("start: %s\n" % self.eval_nuninst(nuninst_start)) self.output_write("start: %s\n" % self.eval_nuninst(nuninst_start))
if not force: if not force:
self.output_write(" orig: %s\n" % self.eval_nuninst(self.nuninst_orig)) self.output_write(" orig: %s\n" % self.eval_nuninst(self.nuninst_orig))
@ -2327,23 +2309,23 @@ class Britney:
if not undo: return if not undo: return
# undo all the changes # undo all the changes
for (undo, pkg, pkg_name, suite) in lundo: for (undo, item) in lundo:
# undo the changes (source) # undo the changes (source)
for k in undo['sources'].keys(): for k in undo['sources'].keys():
if k[0] == '-': if k[0] == '-':
del self.sources['testing'][k[1:]] del self.sources['testing'][k[1:]]
else: self.sources['testing'][k] = undo['sources'][k] else: self.sources['testing'][k] = undo['sources'][k]
for (undo, pkg, pkg_name, suite) in lundo: for (undo, item) in lundo:
# undo the changes (new binaries) # undo the changes (new binaries)
if pkg[0] != '-' and pkg_name in self.sources[suite]: if not item.is_removal and item.package in self.sources[item.suite]:
for p in self.sources[suite][pkg_name][BINARIES]: for p in self.sources[item.suite][item.package][BINARIES]:
binary, arch = p.split("/") binary, arch = p.split("/")
if '/' not in pkg or pkg.endswith("/%s" % (arch)) or pkg.endswith("/%s_tpu" % (arch)) or pkg.endswith("/%s_pu" % (arch)): if item.architecture in ['source', arch]:
del self.binaries['testing'][arch][0][binary] del self.binaries['testing'][arch][0][binary]
self.systems[arch].remove_binary(binary) self.systems[arch].remove_binary(binary)
for (undo, pkg, pkg_name, suite) in lundo: for (undo, item) in lundo:
# undo the changes (binaries) # undo the changes (binaries)
for p in undo['binaries'].keys(): for p in undo['binaries'].keys():
binary, arch = p.split("/") binary, arch = p.split("/")
@ -2357,7 +2339,7 @@ class Britney:
self.systems[arch].add_binary(binary, binaries[binary][:PROVIDES] + \ self.systems[arch].add_binary(binary, binaries[binary][:PROVIDES] + \
[", ".join(binaries[binary][PROVIDES]) or None]) [", ".join(binaries[binary][PROVIDES]) or None])
for (undo, pkg, pkg_name, suite) in lundo: for (undo, item) in lundo:
# undo the changes (virtual packages) # undo the changes (virtual packages)
for p in undo['nvirtual']: for p in undo['nvirtual']:
j, arch = p.split("/") j, arch = p.split("/")
@ -2388,11 +2370,11 @@ class Britney:
if not self.options.actions: if not self.options.actions:
# process `easy' hints # process `easy' hints
for x in self.hints['easy']: for x in self.hints['easy']:
self.do_hint("easy", x[0], x[1]) self.do_hint("easy", x.user, x.packages)
# process `force-hint' hints # process `force-hint' hints
for x in self.hints["force-hint"]: for x in self.hints["force-hint"]:
self.do_hint("force-hint", x[0], x[1]) self.do_hint("force-hint", x.user, x.packages)
# run the first round of the upgrade # run the first round of the upgrade
self.__log("> First loop on the packages with depth = 0", type="I") self.__log("> First loop on the packages with depth = 0", type="I")
@ -2402,7 +2384,7 @@ class Britney:
normpackages = self.upgrade_me[:] normpackages = self.upgrade_me[:]
archpackages = {} archpackages = {}
for a in self.options.break_arches.split(): for a in self.options.break_arches.split():
archpackages[a] = [p for p in normpackages if p.endswith("/" + a) or p.endswith("/" + a + "_tpu") or p.endswith("/" + a + "_pu")] archpackages[a] = [p for p in normpackages if p.architecture == a]
normpackages = [p for p in normpackages if p not in archpackages[a]] normpackages = [p for p in normpackages if p not in archpackages[a]]
self.upgrade_me = normpackages self.upgrade_me = normpackages
self.output_write("info: main run\n") self.output_write("info: main run\n")
@ -2428,7 +2410,7 @@ class Britney:
if hintcnt > 50: if hintcnt > 50:
self.output_write("Skipping remaining hints...") self.output_write("Skipping remaining hints...")
break break
if self.do_hint("hint", x[0], x[1]): if self.do_hint("hint", x.user, x.packages):
hintcnt += 1 hintcnt += 1
# run the auto hinter # run the auto hinter
@ -2442,7 +2424,7 @@ class Britney:
if len(removals) > 0: if len(removals) > 0:
self.output_write("Removing packages left in testing for smooth updates (%d):\n%s" % \ self.output_write("Removing packages left in testing for smooth updates (%d):\n%s" % \
(len(removals), self.old_libraries_format(removals))) (len(removals), self.old_libraries_format(removals)))
self.do_all(actions=removals) self.do_all(actions=[ MigrationItem(x) for x in removals ])
removals = self.old_libraries() removals = self.old_libraries()
else: else:
removals = () removals = ()
@ -2580,38 +2562,38 @@ class Britney:
"hint": 0, "hint": 0,
"force-hint": -1,} "force-hint": -1,}
if isinstance(pkgvers[0], tuple) or isinstance(pkgvers[0], list):
_pkgvers = [ HintItem('%s/%s' % (p, v)) for (p,v) in pkgvers ]
else:
_pkgvers = pkgvers
self.__log("> Processing '%s' hint from %s" % (type, who), type="I") self.__log("> Processing '%s' hint from %s" % (type, who), type="I")
self.output_write("Trying %s from %s: %s\n" % (type, who, " ".join( ["%s/%s" % (p,v) for (p,v) in pkgvers]))) self.output_write("Trying %s from %s: %s\n" % (type, who, " ".join( ["%s/%s" % (x.uvname, x.version) for x in _pkgvers])))
ok = True ok = True
# loop on the requested packages and versions # loop on the requested packages and versions
for pkg, v in pkgvers: for pkg in _pkgvers:
# remove architecture
if "/" in pkg:
pkg = pkg[:pkg.find("/")]
# skip removal requests # skip removal requests
if pkg[0] == "-": if pkg.is_removal:
continue continue
# handle *-proposed-updates # handle *-proposed-updates
elif pkg.endswith("_tpu") or pkg.endswith("_pu"): elif pkg.suite in ['pu', 'tpu']:
pkg, suite = pkg.rsplit("_") if pkg.package not in self.sources[pkg.suite]: continue
if pkg not in self.sources[suite]: continue if apt_pkg.VersionCompare(self.sources[pkg.suite][pkg.package][VERSION], pkg.version) != 0:
if apt_pkg.VersionCompare(self.sources[suite][pkg][VERSION], v) != 0: self.output_write(" Version mismatch, %s %s != %s\n" % (pkg.package, pkg.version, self.sources[pkg.suite][pkg.package][VERSION]))
self.output_write(" Version mismatch, %s %s != %s\n" % (pkg, v, self.sources[suite][pkg][VERSION]))
ok = False ok = False
# does the package exist in unstable? # does the package exist in unstable?
elif pkg not in self.sources['unstable']: elif pkg.package not in self.sources['unstable']:
self.output_write(" Source %s has no version in unstable\n" % pkg) self.output_write(" Source %s has no version in unstable\n" % pkg.package)
ok = False ok = False
elif apt_pkg.VersionCompare(self.sources['unstable'][pkg][VERSION], v) != 0: elif apt_pkg.VersionCompare(self.sources['unstable'][pkg.package][VERSION], pkg.version) != 0:
self.output_write(" Version mismatch, %s %s != %s\n" % (pkg, v, self.sources['unstable'][pkg][VERSION])) self.output_write(" Version mismatch, %s %s != %s\n" % (pkg.package, pkg.version, self.sources['unstable'][pkg.package][VERSION]))
ok = False ok = False
if not ok: if not ok:
self.output_write("Not using hint\n") self.output_write("Not using hint\n")
return False return False
self.do_all(hintinfo[type], map(operator.itemgetter(0), pkgvers)) self.do_all(hintinfo[type], _pkgvers)
return True return True
def sort_actions(self): def sort_actions(self):
@ -2622,7 +2604,7 @@ class Britney:
so the ones with most reverse dependencies are at the end of the loop. so the ones with most reverse dependencies are at the end of the loop.
If an action depends on another one, it is put after it. If an action depends on another one, it is put after it.
""" """
upgrade_me = [x.name for x in self.excuses if x.name in self.upgrade_me] upgrade_me = [x.name for x in self.excuses if x.name in [y.uvname for y in self.upgrade_me]]
for e in self.excuses: for e in self.excuses:
if e.name not in upgrade_me: continue if e.name not in upgrade_me: continue
# try removes at the end of the loop # try removes at the end of the loop
@ -2644,7 +2626,7 @@ class Britney:
self.dependencies[e.name] = e.deps self.dependencies[e.name] = e.deps
# replace the list of actions with the new one # replace the list of actions with the new one
self.upgrade_me = upgrade_me self.upgrade_me = [ MigrationItem(x) for x in upgrade_me ]
def auto_hinter(self): def auto_hinter(self):
"""Auto-generate "easy" hints. """Auto-generate "easy" hints.
@ -2665,7 +2647,7 @@ class Britney:
self.__log("> Processing hints from the auto hinter", type="I") self.__log("> Processing hints from the auto hinter", type="I")
# consider only excuses which are valid candidates # consider only excuses which are valid candidates
excuses = dict([(x.name, x) for x in self.excuses if x.name in self.upgrade_me]) excuses = dict([(x.name, x) for x in self.excuses if x.name in [y.uvname for y in self.upgrade_me]])
def find_related(e, hint, circular_first=False): def find_related(e, hint, circular_first=False):
if e not in excuses: if e not in excuses:
@ -2723,7 +2705,7 @@ class Britney:
to_skip.append(i) to_skip.append(i)
for i in range(len(l)): for i in range(len(l)):
if i not in to_skip: if i not in to_skip:
self.do_hint("easy", "autohinter", l[i]) self.do_hint("easy", "autohinter", [ HintItem("%s/%s" % (x[0], x[1])) for x in l[i] ])
def old_libraries(self): def old_libraries(self):
"""Detect old libraries left in testing for smooth transitions """Detect old libraries left in testing for smooth transitions

@ -0,0 +1,106 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2011 Adam D. Barratt <adsb@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
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
from migrationitem import HintItem
class HintCollection:
def __init__(self):
self._hints = []
def __getitem__(self, type=None):
return self.search(type)
def search(self, type=None, onlyactive=True, package=None, \
version=None, days=None, removal=None):
return [ hint for hint in self._hints if
(type is None or type == hint.type) and
(hint.active or not onlyactive) and
(package is None or package == hint.packages[0].package) and
(version is None or version == hint.packages[0].version) and
(removal is None or removal == hint.packages[0].is_removal)
]
def add_hint(self, hint, user):
self._hints.append(Hint(hint, user))
class Hint:
def __init__(self, hint, user):
self._hint = hint
self._user = user
self._active = True
self._days = None
if isinstance(hint, list):
self._type = hint[0]
self._packages = hint[1:]
else:
self._type, self._packages = hint.split(' ', 1)
if self._type == 'age-days':
if isinstance(hint, list):
self._days = self._packages[0]
self._packages = self._packages[1:]
else:
self._days, self._packages = self._packages.split(' ', 1)
if isinstance(self._packages, str):
self._packages = self._packages.split(' ')
self._packages = [HintItem(x) for x in self._packages]
def set_active(self, active):
self._active = active
def __str__(self):
return self._hint
def __eq__(self, other):
return str(self) == str(other)
@property
def type(self):
return self._type
@property
def packages(self):
return self._packages
@property
def active(self):
return self._active
@property
def user(self):
return self._user
@property
def days(self):
return self._days
@property
def package(self):
if self.packages:
assert len(self.packages) == 1, self.packages
return self.packages[0].package
else:
return None
@property
def version(self):
if self.packages:
assert len(self.packages) == 1, self.packages
return self.packages[0].version
else:
return None

@ -0,0 +1,138 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2011 Adam D. Barratt <adsb@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
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
class MigrationItem:
_architectures = []
@classmethod
def set_architectures(cls, architectures = None):
cls._architectures = architectures or []
@classmethod
def get_architectures(cls):
return cls._architectures
def __init__(self, name = None, versionned = False):
self._name = None
self._uvname = None
self._package = None
self._version = None
self._architecture = None
self._suite = None
self._versionned = versionned
if name:
self._set_name(name)
def __str__(self):
if self._versionned and self.version is not None:
return self.name
else:
return self.uvname
def __eq__(self, other):
isequal = False
if self.uvname == other.uvname:
if self.version is None or other.version is None:
isequal = True
else:
isequal = self.version == other.version
return isequal
def __hash__(self):
return hash((self.uvname, self.version))
def _get_name(self):
return self._name
def _set_name(self, value):
self._version = None
self._name = value
if value.startswith('-'):
value = value[1:]
parts = value.split('/', 3)
package = parts[0]
if '_' in package:
self._package, self._suite = package.split('_', 2)
else:
self._package, self._suite = (package, 'unstable')
if self._versionned and len(parts) > 1:
if len(parts) == 3:
self._architecture = parts[1]
self._version = parts[2]
else:
self._architecture = 'source'
self._version = parts[1]
else:
if len(parts) >= 2:
self._architecture = parts[1]
else:
self._architecture = 'source'
if self._version in self.__class__.get_architectures():
(self._architecture, self._version) = \
(self._version, self._architecture)
if '_' in self._architecture:
self._architecture, self._suite = \
self._architecture.split('_', 2)
if self.is_removal:
self._suite = 'testing'
parts = self._name.split('/', 3)
is_removal = self.is_removal
if len(parts) == 1 or self._architecture == 'source':
self._uvname = self._package
else:
self._uvname = "%s/%s" % (self._package, self._architecture)
if self._suite not in ('testing', 'unstable'):
self._uvname = '%s_%s' % (self._uvname, self._suite)
if is_removal:
self._uvname = '-%s' % (self._uvname)
if self._versionned:
self._name = '%s/%s' % (self._uvname, self._version)
else:
self._name = self._uvname
name = property(_get_name, _set_name)
@property
def is_removal(self):
return self._name.startswith('-')
@property
def architecture(self):
return self._architecture
@property
def package(self):
return self._package
@property
def suite(self):
return self._suite
@property
def version(self):
return self._version
@property
def uvname(self):
return self._uvname
class HintItem(MigrationItem):
def __init__(self, name = None):
MigrationItem.__init__(self, name = name, versionned = True)
Loading…
Cancel
Save