You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

731 lines
32 KiB

#!/usr/bin/env python2.4
# -*- coding: utf-8 -*-
# Copyright (C) 2001-2004 Anthony Towns <ajt@debian.org>
# Andreas Barth <aba@debian.org>
# Fabio Tranchitella <kobold@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.
import os
import re
import sys
import string
import time
import optparse
import apt_pkg
from excuse import Excuse
VERSION = '2.0.alpha1'
class Britney:
"""Debian testing updater script"""
BINARY_FIELDS = ('Version', 'Pre-Depends', 'Depends', 'Conflicts', 'Provides', 'Source', 'Architecture', 'Version')
SOURCE_FIELDS = ('Version', 'Maintainer', 'Section')
HINTS_STANDARD = ("easy", "hint", "remove", "block", "unblock", "urgent", "approve")
HINTS_ALL = ("force", "force-hint", "block-all") + HINTS_STANDARD
def __init__(self):
"""Class constructor method: initialize and populate the data lists"""
self.__parse_arguments()
apt_pkg.init()
self.date_now = int(((time.time() / (60*60)) - 15) / 24)
self.sources = {'testing': self.read_sources(self.options.testing),
'unstable': self.read_sources(self.options.unstable),
'tpu': self.read_sources(self.options.tpu),}
self.binaries = {'testing': {}, 'unstable': {}, 'tpu': {}}
for arch in self.options.architectures:
self.binaries['testing'][arch] = self.read_binaries(self.options.testing, "testing", arch)
self.binaries['unstable'][arch] = self.read_binaries(self.options.unstable, "unstable", arch)
self.binaries['tpu'][arch] = self.read_binaries(self.options.tpu, "tpu", arch)
self.bugs = {'unstable': self.read_bugs(self.options.unstable),
'testing': self.read_bugs(self.options.testing),}
self.normalize_bugs()
self.dates = self.read_dates(self.options.testing)
self.urgencies = self.read_urgencies(self.options.testing)
self.approvals = self.read_approvals(self.options.tpu)
self.hints = self.read_hints(self.options.unstable)
self.excuses = []
def __parse_arguments(self):
"""Parse command line arguments"""
self.parser = optparse.OptionParser(version="%prog " + VERSION)
self.parser.add_option("-v", "", action="count", dest="verbose", help="enable verbose output")
self.parser.add_option("-c", "--config", action="store", dest="config",
default="/etc/britney.conf", help="path for the configuration file")
(self.options, self.args) = self.parser.parse_args()
if not os.path.isfile(self.options.config):
self.__log("Unable to read the configuration file (%s), exiting!" % self.options.config, type="E")
sys.exit(1)
self.MINDAYS = {}
self.HINTS = {}
for k, v in [map(string.strip,r.split('=', 1)) for r in file(self.options.config) if '=' in r and not r.strip().startswith('#')]:
if k.startswith("MINDAYS_"):
self.MINDAYS[k.split("_")[1].lower()] = int(v)
elif k.startswith("HINTS_"):
self.HINTS[k.split("_")[1].lower()] = \
reduce(lambda x,y: x+y, [hasattr(self, "HINTS_" + i) and getattr(self, "HINTS_" + i) or (i,) for i in v.split()])
else:
setattr(self.options, k.lower(), v)
# Sort architectures
allarches = sorted(self.options.architectures.split())
arches = [x for x in allarches if x in self.options.nobreakall_arches]
arches += [x for x in allarches if x not in arches and x not in self.options.fucked_arches]
arches += [x for x in allarches if x not in arches and x not in self.options.break_arches]
arches += [x for x in allarches if x not in arches]
self.options.architectures = arches
def __log(self, msg, type="I"):
"""Print info messages according to verbosity level"""
if self.options.verbose or type in ("E", "W"):
print "%s: [%s] - %s" % (type, time.asctime(), msg)
# Data reading/writing
def read_sources(self, basedir):
"""Read the list of source packages from the specified directory"""
sources = {}
package = None
filename = os.path.join(basedir, "Sources")
self.__log("Loading source packages from %s" % filename)
for l in open(filename):
if l.startswith(' ') or ':' not in l: continue
fields = map(string.strip, l.split(":",1))
if fields[0] == 'Package':
package = fields[1]
sources[package] = dict([(k.lower(), None) for k in self.SOURCE_FIELDS] + [('binaries', [])])
elif fields[0] in self.SOURCE_FIELDS:
sources[package][fields[0].lower()] = fields[1]
return sources
def read_bugs(self, basedir):
"""Read the RC bugs count from the specified directory"""
bugs = {}
filename = os.path.join(basedir, "Bugs")
self.__log("Loading RC bugs count from %s" % filename)
for line in open(filename):
l = line.strip().split()
if len(l) != 2: continue
try:
bugs[l[0]] = int(l[1])
except ValueError:
self.__log("Bugs, unable to parse \"%s\"" % line, type="E")
return bugs
def maxver(self, pkg, dist):
maxver = None
if self.sources[dist].has_key(pkg):
maxver = self.sources[dist][pkg]['version']
for arch in self.options.architectures:
if not self.binaries[dist][arch][0].has_key(pkg): continue
pkgv = self.binaries[dist][arch][0][pkg]['version']
if maxver == None or apt_pkg.VersionCompare(pkgv, maxver) > 0:
maxver = pkgv
return maxver
def normalize_bugs(self):
"""Normalize the RC bugs count for testing and unstable"""
for pkg in set(self.bugs['testing'].keys() + self.bugs['unstable'].keys()):
if not self.bugs['testing'].has_key(pkg):
self.bugs['testing'][pkg] = 0
elif not self.bugs['unstable'].has_key(pkg):
self.bugs['unstable'][pkg] = 0
maxvert = self.maxver(pkg, 'testing')
if maxvert == None or \
self.bugs['testing'][pkg] == self.bugs['unstable'][pkg]:
continue
maxveru = self.maxver(pkg, 'unstable')
if maxveru == None:
continue
elif apt_pkg.VersionCompare(maxvert, maxveru) >= 0:
self.bugs['testing'][pkg] = self.bugs['unstable'][pkg]
def read_dates(self, basedir):
"""Read the upload data for the packages from the specified directory"""
dates = {}
filename = os.path.join(basedir, "Dates")
self.__log("Loading upload data from %s" % filename)
for line in open(filename):
l = line.strip().split()
if len(l) != 3: continue
try:
dates[l[0]] = (l[1], int(l[2]))
except ValueError:
self.__log("Dates, unable to parse \"%s\"" % line, type="E")
return dates
def read_urgencies(self, basedir):
"""Read the upload urgency of the packages from the specified directory"""
urgencies = {}
filename = os.path.join(basedir, "Urgency")
self.__log("Loading upload urgencies from %s" % filename)
for line in open(filename):
l = line.strip().split()
if len(l) != 3: continue
urgency_old = urgencies.get(l[0], self.options.default_urgency)
mindays_old = self.MINDAYS.get(urgency_old, self.MINDAYS[self.options.default_urgency])
mindays_new = self.MINDAYS.get(l[2], self.MINDAYS[self.options.default_urgency])
if mindays_old <= mindays_new:
continue
tsrcv = self.sources['testing'].get(l[0], None)
if tsrcv and apt_pkg.VersionCompare(tsrcv['version'], l[1]) >= 0:
continue
usrcv = self.sources['unstable'].get(l[0], None)
if not usrcv or apt_pkg.VersionCompare(usrcv['version'], l[1]) < 0:
continue
urgencies[l[0]] = l[2]
return urgencies
def read_approvals(self, basedir):
"""Read the approvals data from the specified directory"""
approvals = {}
for approver in self.options.approvers.split():
filename = os.path.join(basedir, "Approved", approver)
self.__log("Loading approvals list from %s" % filename)
for line in open(filename):
l = line.strip().split()
if len(l) != 2: continue
approvals["%s_%s" % (l[0], l[1])] = approver
return approvals
def read_hints(self, basedir):
"""Read the approvals data from the specified directory"""
hints = dict([(k,[]) for k in self.HINTS_ALL])
for who in self.HINTS.keys():
filename = os.path.join(basedir, "Hints", who)
self.__log("Loading hints list from %s" % filename)
for line in open(filename):
line = line.strip()
if line == "": continue
l = line.split()
if l[0] == 'finished':
break
elif l[0] not in self.HINTS[who]:
continue
elif l[0] in ["easy", "hint", "force-hint"]:
hints[l[0]].append((who, [k.split("/") for k in l if "/" in k]))
elif l[0] in ["block-all"]:
hints[l[0]].extend([(y, who) for y in l[1:]])
elif l[0] in ["block"]:
hints[l[0]].extend([(y, who) for y in l[1:]])
elif l[0] in ["remove", "approve", "unblock", "force", "urgent"]:
hints[l[0]].extend([(k.split("/")[0], (k.split("/")[1],who) ) for k in l if "/" in k])
for x in ["block", "block-all", "unblock", "force", "urgent", "remove"]:
z = {}
for a, b in hints[x]:
if z.has_key(a):
self.__log("Overriding %s[%s] = %s with %s" % (x, a, z[a], b), type="W")
z[a] = b
hints[x] = z
return hints
def read_binaries(self, basedir, distribution, arch):
"""Read the list of binary packages from the specified directory"""
packages = {}
package = None
filename = os.path.join(basedir, "Packages_%s" % arch)
self.__log("Loading binary packages from %s" % filename)
for l in open(filename):
if l.startswith(' ') or ':' not in l: continue
fields = map(string.strip, l.split(":",1))
if fields[0] == 'Package':
package = fields[1]
packages[package] = dict([(k.lower(), None) for k in self.BINARY_FIELDS] + [('rdepends', [])])
packages[package]['source'] = package
packages[package]['source-ver'] = None
elif fields[0] == 'Source':
packages[package][fields[0].lower()] = fields[1].split(" ")[0]
if "(" in fields[1]:
packages[package]['source-ver'] = fields[1].split("(")[1].split(")")[0]
elif fields[0] in self.BINARY_FIELDS:
packages[package][fields[0].lower()] = fields[1]
provides = {}
for pkgname in packages:
if not packages[pkgname]['source-ver']:
packages[pkgname]['source-ver'] = packages[pkgname]['version']
if packages[pkgname]['source'] in self.sources[distribution]:
self.sources[distribution][packages[pkgname]['source']]['binaries'].append(pkgname + "/" + arch)
if not packages[pkgname]['provides']:
continue
parts = map(string.strip, packages[pkgname]["provides"].split(","))
del packages[pkgname]["provides"]
for p in parts:
if p in provides:
provides[p].append(pkgname)
else:
provides[p] = [pkgname]
for pkgname in packages:
dependencies = []
if packages[pkgname]['depends']:
packages[pkgname]['depends-txt'] = packages[pkgname]['depends']
packages[pkgname]['depends'] = apt_pkg.ParseDepends(packages[pkgname]['depends'])
dependencies.extend(packages[pkgname]['depends'])
if packages[pkgname]['pre-depends']:
packages[pkgname]['pre-depends-txt'] = packages[pkgname]['pre-depends']
packages[pkgname]['pre-depends'] = apt_pkg.ParseDepends(packages[pkgname]['pre-depends'])
dependencies.extend(packages[pkgname]['pre-depends'])
for p in dependencies:
for a in p:
if a[0] not in packages: continue
packages[a[0]]['rdepends'].append((pkgname, a[1], a[2]))
return (packages, provides)
# Package analisys
def should_remove_source(self, pkg):
"""Check if a source package should be removed from testing"""
if self.sources['unstable'].has_key(pkg):
return False
src = self.sources['testing'][pkg]
excuse = Excuse("-" + pkg)
excuse.set_vers(src['version'], None)
src['maintainer'] and excuse.set_maint(src['maintainer'].strip())
src['section'] and excuse.set_section(src['section'].strip())
excuse.addhtml("Valid candidate")
self.excuses.append(excuse)
return True
def same_source(self, sv1, sv2):
if sv1 == sv2:
return 1
m = re.match(r'^(.*)\+b\d+$', sv1)
if m: sv1 = m.group(1)
m = re.match(r'^(.*)\+b\d+$', sv2)
if m: sv2 = m.group(1)
if sv1 == sv2:
return 1
if re.search("-", sv1) or re.search("-", sv2):
m = re.match(r'^(.*-[^.]+)\.0\.\d+$', sv1)
if m: sv1 = m.group(1)
m = re.match(r'^(.*-[^.]+\.[^.]+)\.\d+$', sv1)
if m: sv1 = m.group(1)
m = re.match(r'^(.*-[^.]+)\.0\.\d+$', sv2)
if m: sv2 = m.group(1)
m = re.match(r'^(.*-[^.]+\.[^.]+)\.\d+$', sv2)
if m: sv2 = m.group(1)
return (sv1 == sv2)
else:
m = re.match(r'^([^-]+)\.0\.\d+$', sv1)
if m and sv2 == m.group(1): return 1
m = re.match(r'^([^-]+)\.0\.\d+$', sv2)
if m and sv1 == m.group(1): return 1
return 0
def get_dependency_solvers(self, block, arch, distribution):
packages = []
for name, version, op in block:
real_package = False
if name in self.binaries[distribution][arch][0]:
real_package = True
package = self.binaries[distribution][arch][0][name]
if op == '' and version == '' or apt_pkg.CheckDep(package['version'], op, version):
packages.append(name)
# TODO: this would be enough according to policy, but not according to britney v.1
#if op == '' and version == '' and name in self.binaries[distribution][arch][1]:
# # packages.extend(self.binaries[distribution][arch][1][name])
# return (True, packages)
if name in self.binaries[distribution][arch][1]:
for prov in self.binaries[distribution][arch][1][name]:
package = self.binaries[distribution][arch][0][prov]
if op == '' and version == '' or apt_pkg.CheckDep(package['version'], op, version):
packages.append(prov)
break
return (len(packages) > 0, packages)
def excuse_unsat_deps(self, pkg, src, arch, suite, excuse, ignore_break=0):
binary_u = self.binaries[suite][arch][0][pkg]
for type in ('Pre-Depends', 'Depends'):
type_key = type.lower()
if not binary_u[type_key]:
continue
packages = []
for block, block_txt in map(None, binary_u[type_key], binary_u[type_key + '-txt'].split(',')):
solved, packages = self.get_dependency_solvers(block, arch, 'testing')
if solved: continue
solved, packages = self.get_dependency_solvers(block, arch, suite)
packages = [self.binaries[suite][arch][0][p]['source'] for p in packages]
if src in packages: continue
if len(packages) == 0:
excuse.addhtml("%s/%s unsatisfiable %s: %s" % (pkg, arch, type, block_txt.strip()))
for p in packages:
if ignore_break or arch not in self.options.break_arches.split():
excuse.add_dep(p)
else:
excuse.add_break_dep(p, arch)
def should_upgrade_srcarch(self, src, arch, suite):
# binnmu this arch?
source_t = self.sources['testing'][src]
source_u = self.sources[suite][src]
ref = "%s/%s%s" % (src, arch, suite != 'unstable' and "_" + suite or "")
excuse = Excuse(ref)
excuse.set_vers(source_t['version'], source_t['version'])
source_u['maintainer'] and excuse.set_maint(source_u['maintainer'].strip())
source_u['section'] and excuse.set_section(source_u['section'].strip())
anywrongver = False
anyworthdoing = False
if self.hints["remove"].has_key(src) and \
self.same_source(source_t['version'], self.hints["remove"][src][0]):
excuse.addhtml("Removal request by %s" % (self.hints["remove"][src][1]))
excuse.addhtml("Trying to remove package, not update it")
excuse.addhtml("Not considered")
self.excuses.append(excuse)
return False
for pkg in sorted(source_u['binaries']):
if not pkg.endswith("/" + arch): continue
pkg_name = pkg.split("/")[0]
binary_t = pkg in source_t['binaries'] and self.binaries['testing'][arch][0][pkg_name] or None
binary_u = self.binaries[suite][arch][0][pkg_name]
pkgsv = self.binaries[suite][arch][0][pkg_name]['source-ver']
if binary_u['architecture'] == 'all':
excuse.addhtml("Ignoring %s %s (from %s) as it is arch: all" % (pkg_name, binary_u['version'], pkgsv))
continue
if not self.same_source(source_t['version'], pkgsv):
anywrongver = True
excuse.addhtml("From wrong source: %s %s (%s not %s)" % (pkg_name, binary_u['version'], pkgsv, source_t['version']))
break
self.excuse_unsat_deps(pkg_name, src, arch, suite, excuse)
if not binary_t:
excuse.addhtml("New binary: %s (%s)" % (pkg_name, binary_u['version']))
anyworthdoing = True
continue
vcompare = apt_pkg.VersionCompare(binary_t['version'], binary_u['version'])
if vcompare > 0:
anywrongver = True
excuse.addhtml("Not downgrading: %s (%s to %s)" % (pkg_name, binary_t['version'], binary_u['version']))
break
elif vcompare < 0:
excuse.addhtml("Updated binary: %s (%s to %s)" % (pkg_name, binary_t['version'], binary_u['version']))
anyworthdoing = True
if not anywrongver and (anyworthdoing or src in self.sources[suite]):
srcv = self.sources[suite][src]['version']
ssrc = self.same_source(source_t['version'], srcv)
for pkg in sorted([x.split("/")[0] for x in self.sources['testing'][src]['binaries'] if x.endswith("/"+arch)]):
if self.binaries['testing'][arch][0][pkg]['architecture'] == 'all':
excuse.addhtml("Ignoring removal of %s as it is arch: all" % (pkg))
continue
if not self.binaries[suite][arch][0].has_key(pkg):
tpkgv = self.binaries['testing'][arch][0][pkg]['version']
excuse.addhtml("Removed binary: %s %s" % (pkg, tpkgv))
if ssrc: anyworthdoing = True
if not anywrongver and anyworthdoing:
excuse.addhtml("Valid candidate")
self.excuses.append(excuse)
elif anyworthdoing:
excuse.addhtml("Not considered")
self.excuses.append(excuse)
return False
return True
def should_upgrade_src(self, src, suite):
source_u = self.sources[suite][src]
if src in self.sources['testing']:
source_t = self.sources['testing'][src]
if apt_pkg.VersionCompare(source_t['version'], source_u['version']) == 0:
# Candidate for binnmus only
return False
else:
source_t = None
ref = "%s%s" % (src, suite != 'unstable' and "_" + suite or "")
update_candidate = True
excuse = Excuse(ref)
excuse.set_vers(source_t and source_t['version'] or None, source_u['version'])
source_u['maintainer'] and excuse.set_maint(source_u['maintainer'].strip())
source_u['section'] and excuse.set_section(source_u['section'].strip())
if source_t and apt_pkg.VersionCompare(source_u['version'], source_t['version']) < 0:
# Version in unstable is older!
excuse.addhtml("ALERT: %s is newer in testing (%s %s)" % (src, source_t['version'], source_u['version']))
self.excuses.append(excuse)
return False
urgency = self.urgencies.get(src, self.options.default_urgency)
if not source_t and urgency != self.options.default_urgency:
excuse.addhtml("Ignoring %s urgency setting for NEW package" % (urgency))
urgency = self.options.default_urgency
if self.hints["remove"].has_key(src):
if source_t and self.same_source(source_t['version'], self.hints['remove'][src][0]) or \
self.same_source(source_u['version'], self.hints['remove'][src][0]):
excuse.addhtml("Removal request by %s" % (self.hints["remove"][src][1]))
excuse.addhtml("Trying to remove package, not update it")
update_candidate = False
blocked = None
if self.hints["block"].has_key(src):
blocked = self.hints["block"][src]
elif self.hints["block-all"].has_key("source"):
blocked = self.hints["block-all"]["source"]
if blocked:
unblock = self.hints["unblock"].get(src,(None,None))
if unblock[0] != None and self.same_source(unblock[0], source_u['version']):
excuse.addhtml("Ignoring request to block package by %s, due to unblock request by %s" % (blocked, unblock[1]))
else:
if unblock[0] != None:
excuse.addhtml("Unblock request by %s ignored due to version mismatch: %s" % (unblock[1], unblock[0]))
excuse.addhtml("Not touching package, as requested by %s (contact debian-release if update is needed)" % (blocked))
update_candidate = False
if suite == 'unstable':
if not self.dates.has_key(src):
self.dates[src] = (source_u['version'], self.date_now)
elif not self.same_source(self.dates[src][0], source_u['version']):
self.dates[src] = (source_u['version'], self.date_now)
days_old = self.date_now - self.dates[src][1]
min_days = self.MINDAYS[urgency]
excuse.setdaysold(days_old, min_days)
if days_old < min_days:
if self.hints["urgent"].has_key(src) and self.same_source(source_u['version'], self.hints["urgent"][src][0]):
excuse.addhtml("Too young, but urgency pushed by %s" % (self.hints["urgent"][src][1]))
else:
update_candidate = False
pkgs = {src: ["source"]}
for arch in self.options.architectures:
oodbins = {}
for pkg in sorted([x.split("/")[0] for x in self.sources[suite][src]['binaries'] if x.endswith("/"+arch)]):
if not pkgs.has_key(pkg): pkgs[pkg] = []
pkgs[pkg].append(arch)
binary_u = self.binaries[suite][arch][0][pkg]
pkgsv = binary_u['source-ver']
if not self.same_source(source_u['version'], pkgsv):
if not oodbins.has_key(pkgsv):
oodbins[pkgsv] = []
oodbins[pkgsv].append(pkg)
continue
if binary_u['architecture'] != 'all' or arch in self.options.nobreakall_arches:
self.excuse_unsat_deps(pkg, src, arch, suite, excuse)
if oodbins:
oodtxt = ""
for v in oodbins.keys():
if oodtxt: oodtxt = oodtxt + "; "
oodtxt = oodtxt + "%s (from <a href=\"http://buildd.debian.org/build.php?arch=%s&pkg=%s&ver=%s\" target=\"_blank\">%s</a>)" % (", ".join(sorted(oodbins[v])), arch, src, v, v)
text = "out of date on <a href=\"http://buildd.debian.org/build.php?arch=%s&pkg=%s&ver=%s\" target=\"_blank\">%s</a>: %s" % (arch, src, source_u['version'], arch, oodtxt)
if arch in self.options.fucked_arches:
text = text + " (but %s isn't keeping up, so nevermind)" % (arch)
else:
update_candidate = False
if self.date_now != self.dates[src][1]:
excuse.addhtml(text)
if len(self.sources[suite][src]['binaries']) == 0:
excuse.addhtml("%s has no binaries on any arch" % src)
update_candidate = False
if suite == 'unstable':
for pkg in pkgs.keys():
if not self.bugs['testing'].has_key(pkg):
self.bugs['testing'][pkg] = 0
if not self.bugs['unstable'].has_key(pkg):
self.bugs['unstable'][pkg] = 0
if self.bugs['unstable'][pkg] > self.bugs['testing'][pkg]:
excuse.addhtml("%s (%s) is <a href=\"http://bugs.debian.org/cgi-bin/pkgreport.cgi?which=pkg&data=%s&sev-inc=critical&sev-inc=grave&sev-inc=serious\" target=\"_blank\">buggy</a>! (%d > %d)" % (pkg, ", ".join(pkgs[pkg]), pkg, self.bugs['unstable'][pkg], self.bugs['testing'][pkg]))
update_candidate = False
elif self.bugs['unstable'][pkg] > 0:
excuse.addhtml("%s (%s) is (less) <a href=\"http://bugs.debian.org/cgi-bin/pkgreport.cgi?which=pkg&data=%s&sev-inc=critical&sev-inc=grave&sev-inc=serious\" target=\"_blank\">buggy</a>! (%d <= %d)" % (pkg, ", ".join(pkgs[pkg]), pkg, self.bugs['unstable'][pkg], self.bugs['testing'][pkg]))
if not update_candidate and self.hints["force"].has_key(src) and self.same_source(source_u['version'], self.hints["force"][src][0]) :
excuse.dontinvalidate = 1
excuse.addhtml("Should ignore, but forced by %s" % (self.hints["force"][src][1]))
update_candidate = True
if suite == "tpu":
if self.approvals.has_key("%s_%s" % (src, source_u['version'])):
excuse.addhtml("Approved by %s" % approvals["%s_%s" % (src, source_u['version'])])
else:
excuse.addhtml("NEEDS APPROVAL BY RM")
update_candidate = False
if update_candidate:
excuse.addhtml("Valid candidate")
else:
excuse.addhtml("Not considered")
self.excuses.append(excuse)
return update_candidate
def reversed_exc_deps(self):
res = {}
for exc in self.excuses:
for d in exc.deps:
if not res.has_key(d): res[d] = []
res[d].append(exc.name)
return res
def invalidate_excuses(self, valid, invalid):
i = 0
exclookup = {}
for e in self.excuses:
exclookup[e.name] = e
revdeps = self.reversed_exc_deps()
while i < len(invalid):
if not revdeps.has_key(invalid[i]):
i += 1
continue
if (invalid[i] + "_tpu") in valid:
i += 1
continue
for x in revdeps[invalid[i]]:
if x in valid and exclookup[x].dontinvalidate:
continue
exclookup[x].invalidate_dep(invalid[i])
if x in valid:
p = valid.index(x)
invalid.append(valid.pop(p))
exclookup[x].addhtml("Invalidated by dependency")
exclookup[x].addhtml("Not considered")
i = i + 1
def main(self):
"""Main method, entry point for the analisys"""
upgrade_me = []
# Packages to be removed
for pkg in self.sources['testing']:
if self.should_remove_source(pkg):
upgrade_me.append("-" + pkg)
# Packages to be upgraded from unstable
for pkg in self.sources['unstable']:
if self.sources['testing'].has_key(pkg):
for arch in self.options.architectures:
if self.should_upgrade_srcarch(pkg, arch, 'unstable'):
upgrade_me.append("%s/%s" % (pkg, arch))
if self.should_upgrade_src(pkg, 'unstable'):
upgrade_me.append(pkg)
# Packages to be upgraded from testing-proposed-updates
for pkg in self.sources['tpu']:
if self.sources['testing'].has_key(pkg):
for arch in self.options.architectures:
if self.should_upgrade_srcarch(pkg, arch, 'tpu'):
upgrade_me.append("%s/%s_tpu" % (pkg, arch))
if self.should_upgrade_src(pkg, 'tpu'):
upgrade_me.append("%s_tpu" % pkg)
# Process 'remove' hints
for src in self.hints["remove"].keys():
if src in upgrade_me: continue
if ("-"+src) in upgrade_me: continue
if not self.sources['testing'].has_key(src): continue
tsrcv = self.sources['testing'][src]['version']
if not self.same_source(tsrcv, self.hints["remove"][src][0]): continue
upgrade_me.append("-%s" % (src))
excuse = Excuse("-%s" % (src))
excuse.set_vers(tsrcv, None)
excuse.addhtml("Removal request by %s" % (self.hints["remove"][src][1]))
excuse.addhtml("Package is broken, will try to remove")
self.excuses.append(excuse)
# Sort excuses by daysold and name
self.excuses.sort(lambda x, y: cmp(x.daysold, y.daysold) or cmp(x.name, y.name))
# Extract unconsidered packages
unconsidered = [e.name for e in self.excuses if e.name not in upgrade_me]
# Invalidate impossible excuses
for e in self.excuses:
for d in e.deps:
if d not in upgrade_me and d not in unconsidered:
e.addhtml("Unpossible dep: %s -> %s" % (e.name, d))
self.invalidate_excuses(upgrade_me, unconsidered)
# Write excuses
f = open(self.options.excuses_output, 'w')
f.write("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n")
f.write("<html><head><title>excuses...</title>")
f.write("<meta http-equiv=\"Content-Type\" content=\"text/html;charset=utf-8\"></head><body>\n")
f.write("<p>Generated: " + time.strftime("%Y.%m.%d %H:%M:%S %z", time.gmtime(time.time())) + "</p>\n")
f.write("<ul>\n")
for e in self.excuses:
f.write("<li>%s" % e.html())
f.write("</ul></body></html>\n")
f.close()
del self.excuses
# Some examples ...
# print self.sources['testing']['zsh-beta']['version']
# print self.sources['unstable']['zsh-beta']['version']
# print self.urgencies['zsh-beta']
# Which packages depend on passwd?
# for i in self.binaries['testing']['i386'][0]['passwd']['rdepends']:
# print i
# Which packages provide mysql-server?
# for i in self.binaries['testing']['i386'][1]['mysql-server']:
# print i
# Which binary packages are build from php4 testing source package?
# print self.sources['testing']['php4']['binaries']
if __name__ == '__main__':
Britney().main()