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.

1329 lines
35 KiB

#!/usr/bin/env python
# Copyright 2001-6 Anthony Towns
# 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 sys, re, string, time, whrandom, math
import britney
if len(sys.argv) != 4:
print "Must specify testing, unstable, testing-updates directories."
sys.exit(1)
testingdir = sys.argv[1]
unstabledir = sys.argv[2]
testingupdatesdir = sys.argv[3]
# Configuration information
expected_arches = 12
allarches = [ 'i386', 'sparc', 'alpha', 'powerpc', 'arm', 'm68k', 'hppa', 'ia64', 'mips', 'mipsel', 's390', 'amd64' ]
mindays = { "low" : 10, "medium" : 5, "high" : 2, "critical" : 0,
"emergency" : 0 }
defaulturgency = "low"
# if you're not in this list, arch: all packages are allowed to break on you
nobreakarchallarches = ['i386']
# if you're in this list, your packages may not stay in sync with the source
fuckedarches = ['m68k', 'sparc']
# if you're in this list, your uninstallability count may increase
breakarches = ['m68k', 'sparc']
# new architectures
newarches = []
allarches.sort()
arches = [ x for x in allarches if x in nobreakarchallarches ]
arches += [ x for x in allarches if x not in arches and x not in fuckedarches ]
arches += [ x for x in allarches if x not in arches and x not in breakarches ]
arches += [ x for x in allarches if x not in arches and x not in newarches ]
arches += [ x for x in allarches if x not in arches ]
# Subs
def same_source(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 read_approvals(dir, approver, approved):
f = open("%s/%s" % (dir, approver))
line = f.readline()
while line:
l = string.split(line)
if len(l) == 2:
[pkg,ver] = l
approved["%s_%s" % (pkg, ver)] = approver
line = f.readline()
f.close()
def read_bugs(file):
bugsperpkg = {}
f = open(file)
line = f.readline()
while line:
l = string.split(line)
if len(l) == 2:
bugsperpkg[l[0]] = string.atoi(l[1])
line = f.readline()
f.close()
return bugsperpkg
def write_bugs(file, bugs):
f = open(file, 'w')
pkgs = bugs.keys()
pkgs.sort()
for pkg in pkgs:
if bugs[pkg] == 0: continue
f.write("%s %d\n" % (pkg, bugs[pkg]))
f.close()
def read_dates(file):
dates = {}
f = open(file)
line = f.readline()
while line:
l = string.split(line)
if len(l) == 3:
dates[l[0]] = (l[1], string.atoi(l[2]))
line = f.readline()
f.close()
return dates
def write_dates(file, dates):
f = open(file, 'w')
pkgs = dates.keys()
pkgs.sort()
for pkg in dates.keys():
f.write("%s %s %d\n" % ((pkg,) + dates[pkg]))
f.close()
def read_urgencies(file, testing, unstable):
urgency = {}
f = open(file)
line = f.readline()
while line:
l = string.split(line)
if len(l) == 3:
uo = urgency.get(l[0], defaulturgency)
mo = mindays.get(uo, mindays[defaulturgency])
mn = mindays.get(l[2], mindays[defaulturgency])
if mo <= mn:
line = f.readline()
continue
tsrcv = testing.get_version(l[0])
if tsrcv and britney.versioncmp(tsrcv, l[1]) >= 0:
line = f.readline()
continue
usrcv = unstable.get_version(l[0])
if not usrcv or britney.versioncmp(usrcv, l[1]) < 0:
line = f.readline()
continue
urgency[l[0]] = l[2]
line = f.readline()
f.close()
return urgency
def read_hints(dir, hinter, hints, allowed):
res = {}
for k in allowed:
res[k] = []
try:
f = open("%s/%s" % (dir, hinter))
except IOError:
return res
while 1:
line = f.readline()
if not line: break
l = string.split(line)
if len(l) == 0 or line[0] == "#":
continue
type = l[0]
if type == "finished":
break
if type not in allowed:
continue
def mysplit(str):
x = str.rfind("/")
if x == -1: return [str]
return [str[:x], str[x+1:]]
if type in ["easy", "hint", "force-hint"]:
l = [ tuple(mysplit(y)) for y in l[1:] ]
l = [ k for k in l if len(k) == 2 ]
res[type].append((hinter, l))
if type in ["block-all"]:
l = [ (y, hinter) for y in l[1:] ]
res[type].extend(l)
if type in ["block"]:
l = [ (y, hinter) for y in l[1:] ]
res[type].extend(l)
if type in ["remove", "approve", "unblock", "force", "urgent"]:
l = [ tuple(mysplit(y)+[hinter]) for y in l[1:] ]
l = [ k for k in l if len(k) == 3 ]
l = [ (p, (v,h)) for (p,v,h) in l ]
res[type].extend(l)
f.close()
return res
class Excuse:
reemail = re.compile(r"<.*?>")
def __init__(self, name):
self.name = name
self.ver = ("-", "-")
self.maint = None
self.pri = None
self.date = None
self.urgency = None
self.daysold = None
self.mindays = None
self.section = None
self.dontinvalidate = 0
self.invalid_deps = []
self.deps = []
self.break_deps = []
self.bugs = []
self.htmlline = []
def set_vers(self, tver, uver):
if tver: self.ver = (tver, self.ver[1])
if uver: self.ver = (self.ver[0], uver)
def set_maint(self, maint):
self.maint = self.reemail.sub("",maint)
# self.maint = maint
def set_section(self, section):
self.section = section
def set_priority(self, pri):
self.pri = pri
def set_date(self, date):
self.date = date
def set_urgency(self, date):
self.urgency = date
def add_dep(self, name):
if name not in self.deps: self.deps.append(name)
def add_break_dep(self, name, arch):
if (name, arch) not in self.break_deps:
self.break_deps.append( (name, arch) )
def invalidate_dep(self, name):
if name not in self.invalid_deps: self.invalid_deps.append(name)
def setdaysold(self, daysold, mindays):
self.daysold = daysold
self.mindays = mindays
def addhtml(self, note):
self.htmlline.append(note)
def html(self):
res = "<a id=\"%s\" name=\"%s\">%s</a> (%s to %s)\n<ul>\n" % \
(self.name, self.name, self.name, self.ver[0], self.ver[1])
if self.maint:
res = res + "<li>Maintainer: %s\n" % (self.maint)
if self.section and string.find(self.section, "/") > -1:
res = res + "<li>Section: %s\n" % (self.section)
if self.daysold != None:
if self.daysold < self.mindays:
res = res + ("<li>Too young, only %d of %d days old\n" %
(self.daysold, self.mindays))
else:
res = res + ("<li>%d days old (needed %d days)\n" %
(self.daysold, self.mindays))
for x in self.htmlline:
res = res + "<li>" + x + "\n"
for x in self.deps:
if x in self.invalid_deps:
res = res + "<li>Depends: %s <a href=\"#%s\">%s</a> (not considered)\n" % (self.name, x, x)
else:
res = res + "<li>Depends: %s <a href=\"#%s\">%s</a>\n" % (self.name, x, x)
for (n,a) in self.break_deps:
if n not in self.deps:
res += "<li>Ignoring %s depends: <a href=\"#%s\">%s</a>\n" % (a, n, n)
res = res + "</ul>\n"
return res
def should_remove_source(src, orig, new, excs):
if new.is_present(src): return 0
okay = 1
exc = Excuse("-" + src)
exc.set_vers(orig.get_version(src), None)
m = orig.get_field(src, "Maintainer")
if m: exc.set_maint(string.strip(m))
s = orig.get_field(src, "Section")
if s: exc.set_section(string.strip(s))
if hints["block"].has_key("-" + src):
blocked = hints["block"]["-" + src]
exc.addhtml("Not touching package, as requested by %s (contact debian-release if update is needed)" % (blocked))
okay = 0
if okay:
exc.addhtml("Valid candidate")
else:
exc.addhtml("Not considered")
excs.append(exc)
return okay
def should_upgrade_srcarch(src, arch, suite, tsrcv, orig, opkgsa, new, npkgsa, excs):
# binnmu this arch?
anywrongver = 0
anyworthdoing = 0
ref = "%s/%s" % (src, arch)
if suite: ref = ref + "_%s" % (suite)
e = Excuse(ref)
e.set_vers(tsrcv, tsrcv)
m = new.get_field(src, "Maintainer")
if m: e.set_maint(string.strip(m))
s = new.get_field(src, "Section")
if s: e.set_section(string.strip(s))
if hints["remove"].has_key(src):
if same_source(tsrcv, hints["remove"][src][0]):
e.addhtml("Removal request by %s" %
(hints["remove"][src][1]))
e.addhtml("Trying to remove package, not update it")
e.addhtml("Not considered")
excs.append(e)
return 0
for pkg in new.binaries(src, arch):
pkgv = npkgsa.get_version(pkg)
pkgsv = npkgsa.get_sourcever(pkg)
if npkgsa.is_arch_all(pkg):
e.addhtml("Ignoring %s %s (from %s) as it is arch: all"
% (pkg, pkgv, pkgsv))
continue
if not same_source(tsrcv, pkgsv):
anywrongver = 1
e.addhtml("From wrong source: %s %s (%s not %s)" % (
pkg, pkgv, pkgsv, tsrcv))
break
excuse_unsat_deps(pkg, arch, opkgsa, npkgsa, e, 0)
if not opkgsa.is_present(pkg):
e.addhtml("New binary: %s (%s)" % (pkg, pkgv))
anyworthdoing = 1
continue
tpkgv = opkgsa.get_version(pkg)
if britney.versioncmp(tpkgv, pkgv) > 0:
anywrongver = 1
e.addhtml("Not downgrading: %s (%s to %s)" % (
pkg, tpkgv, pkgv))
break
elif britney.versioncmp(tpkgv, pkgv) < 0:
e.addhtml("Updated binary: %s (%s to %s)" % (
pkg, tpkgv, pkgv))
anyworthdoing = 1
if not anywrongver and (anyworthdoing or not new.is_fake(src)):
srcv = new.get_version(src)
ssrc = same_source(tsrcv, srcv)
for pkg in orig.binaries(src, arch):
if opkgsa.is_arch_all(pkg):
e.addhtml("Ignoring removal of %s as it is arch: all"
% (pkg))
continue
if not npkgsa.is_present(pkg):
tpkgv = opkgsa.get_version(pkg)
e.addhtml("Removed binary: %s %s" % (
pkg, tpkgv))
if ssrc: anyworthdoing = 1
if not anywrongver and anyworthdoing:
e.addhtml("Valid candidate")
excs.append(e)
return 1
else:
if anyworthdoing:
e.addhtml("Not considered")
excs.append(e)
return 0
def excuse_unsat_deps(pkg, arch, tpkgsarch, upkgsarch, exc, ignore_break=0):
for d in ['Pre-Depends', 'Depends']:
udt = tpkgsarch.unsatisfiable_deps(upkgsarch, pkg, d)
udu = upkgsarch.unsatisfiable_deps(upkgsarch, pkg, d)
for t,u in map(None, udt, udu):
if t[1]: continue
l = []
for e in u[1]:
s = upkgsarch.get_source(e)
if s not in l: l.append(s)
if src in l: continue
if l == []:
exc.addhtml("%s/%s unsatisfiable %s: %s" % (pkg, arch, d, t[0]))
for s in l:
if ignore_break or arch not in breakarches:
exc.add_dep(s)
else:
exc.add_break_dep(s, arch)
def should_upgrade_src(src, suite, orig, origpkgs, new, newpkgs, approvals,
excs):
srcv = new.get_version(src)
if orig.is_present(src):
tsrcv = orig.get_version(src)
if britney.versioncmp(srcv, tsrcv) == 0:
# Candidate for binnmus only
return 0
else:
tsrcv = None
updatecand = 1
ref = src
if suite: ref = ref + "_tpu"
exc = Excuse(ref)
exc.set_vers(tsrcv, srcv)
m = new.get_field(src, "Maintainer")
if m: exc.set_maint(string.strip(m))
s = new.get_field(src, "Section")
if s: exc.set_section(string.strip(s))
if tsrcv and britney.versioncmp(srcv, tsrcv) < 0:
# Version in unstable is older!
exc.addhtml("ALERT: %s is newer in testing (%s %s)" % (src, tsrcv, srcv))
excs.append(exc)
return 0
if unstable.is_fake(src):
exc.addhtml("%s source package doesn't exist" % (src))
updatecand = 0
urgency = unstableurg.get(src, defaulturgency)
if not tsrcv and urgency != defaulturgency:
exc.addhtml("Ignoring %s urgency setting for NEW package" % (urgency))
urgency = defaulturgency
if hints["remove"].has_key(src):
if (tsrcv and same_source(tsrcv, hints["remove"][src][0])) or \
same_source(srcv, hints["remove"][src][0]):
exc.addhtml("Removal request by %s" %
(hints["remove"][src][1]))
exc.addhtml("Trying to remove package, not update it")
updatecand = 0
blocked = None
if hints["block"].has_key(src):
blocked = hints["block"][src]
elif hints["block-all"].has_key("source"):
blocked = hints["block-all"]["source"]
if blocked:
ubv = hints["unblock"].get(src,(None,None))[0]
if ubv != None and same_source(ubv, srcv):
exc.addhtml("Ignoring request to block package by %s, due to unblock request by %s" % (blocked, hints["unblock"][src][1]))
else:
if ubv != None:
exc.addhtml("Unblock request by %s ignored due to version mismatch: %s" % (hints["unblock"][src][1], hints["unblock"][src][0]))
exc.addhtml("Not touching package, as requested by %s (contact debian-release if update is needed)" % (blocked))
updatecand = 0
if suite == None:
if not unstabledates.has_key(src):
unstabledates[src] = (srcv, datenow)
elif not same_source(unstabledates[src][0], srcv):
unstabledates[src] = (srcv, datenow)
daysold = datenow - unstabledates[src][1]
mymindays = mindays[urgency]
exc.setdaysold(daysold, mymindays)
if daysold < mymindays:
if hints["urgent"].has_key(src) and same_source(srcv, hints["urgent"][src][0]):
exc.addhtml("Too young, but urgency pushed by %s" % (hints["urgent"][src][1]))
else:
updatecand = 0
pkgs = { src: ["source"] }
anybins = 0
for arch in arches:
oodbins = {}
for pkg in new.binaries(src,arch):
anybins = 1
if not pkgs.has_key(pkg): pkgs[pkg] = []
pkgs[pkg].append(arch)
pkgsv = newpkgs[arch].get_sourcever(pkg)
if not same_source(srcv, pkgsv):
if not oodbins.has_key(pkgsv):
oodbins[pkgsv] = []
oodbins[pkgsv].append(pkg)
continue
if newpkgs[arch].isnt_arch_all(pkg) or \
arch in nobreakarchallarches:
excuse_unsat_deps(pkg, arch,
origpkgs[arch], newpkgs[arch], exc)
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(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, srcv, arch, oodtxt)
if arch in fuckedarches:
text = text + " (but %s isn't keeping up," % \
(arch) + " so nevermind)"
else:
updatecand = 0
if datenow != unstabledates[src][1]:
exc.addhtml(text)
if not anybins:
exc.addhtml("%s has no binaries on any arch" % src)
updatecand = 0
if suite == None:
for pkg in pkgs.keys():
if not testingbugs.has_key(pkg): testingbugs[pkg] = 0
if not unstablebugs.has_key(pkg): unstablebugs[pkg] = 0
if unstablebugs[pkg] > testingbugs[pkg]:
exc.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,
unstablebugs[pkg], testingbugs[pkg]))
updatecand = 0
elif unstablebugs[pkg] > 0:
exc.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,
unstablebugs[pkg], testingbugs[pkg]))
if not updatecand and hints["force"].has_key(src) and same_source(srcv, hints["force"][src][0]) :
exc.dontinvalidate = 1
exc.addhtml("Should ignore, but forced by %s" % (hints["force"][src][1]))
updatecand = 1
if approvals:
if approvals.has_key("%s_%s" % (src, srcv)):
exc.addhtml("Approved by %s" %
approvals["%s_%s" % (src, srcv)])
else:
exc.addhtml("NEEDS APPROVAL BY RM")
updatecand = 0
if updatecand:
exc.addhtml("Valid candidate")
else:
exc.addhtml("Not considered")
excuses.append(exc)
return updatecand
###
# Brute force stuff
class UpgradeRun:
def __init__(self, sn, u, tu, ps):
self.srcsn = sn
self.unstable = u
self.testingupdates = tu
self.packages = ps
self.sortpkgs()
self.output = open("update.OUTPUT_py", "w");
self.arches = [ x for x in arches if x in srcsn.arches ]
self.srcsnpkgs = {}
for arch in arches:
self.srcsnpkgs[arch] = self.srcsn.Packages(arch)
#def __del__():
# self.output.close()
def sortpkgs(self):
p = self.packages
p.sort()
self.packages = p
def writeout(self, text):
self.output.write(text)
sys.stdout.write(text)
self.output.flush()
sys.stdout.flush()
def doop_source(self, op):
# removals = "-<source>",
# arch = "<source>/<arch>",
# normal = "<source>"
which = self.unstable
if "_" in op:
ind = string.index(op, "_")
if op[ind+1:] == "tpu":
which = self.testingupdates
op = op[:ind]
if op[0] == "-":
self.srcsn.remove_source(op[1:])
elif "/" in op:
ind = string.index(op, "/")
self.srcsn.upgrade_arch(which, op[:ind], op[ind+1:])
else:
self.srcsn.upgrade_source(which, op)
def get_nuninst(self):
nuninst = {}
for arch in self.arches:
con = self.srcsnpkgs[arch].packages
if arch not in nobreakarchallarches:
con = filter(
self.srcsnpkgs[arch].isnt_arch_all,
con)
nuninst[arch] = filter(
self.srcsnpkgs[arch].is_uninstallable,
con)
return nuninst
def get_improved_nuninst(self, old):
new = {}
for arch in self.arches:
con = self.srcsnpkgs[arch].packages
if arch not in nobreakarchallarches:
con = filter(
self.srcsnpkgs[arch].isnt_arch_all,
con)
new[arch] = filter(
self.srcsnpkgs[arch].is_uninstallable, con)
if arch in breakarches: continue
if len(new[arch]) > len(old[arch]):
return (0, new)
return (1, new)
def arch_improved_nuninst(self, old, arch):
new = old.copy()
if "_" in arch: arch = arch[:arch.index("_")]
con = self.srcsnpkgs[arch].packages
if arch not in nobreakarchallarches:
con = filter(self.srcsnpkgs[arch].isnt_arch_all, con)
new[arch] = filter(self.srcsnpkgs[arch].is_uninstallable, con)
if arch not in newarches and len(new[arch]) > len(old[arch]):
return (0, new)
return (1, new)
def is_nuninst_asgood(self, old, new):
for arch in self.arches:
if arch in breakarches: continue
if len(new[arch]) > len(old[arch]):
return 0
return 1
def is_nuninst_asgood_generous(self, old, new):
diff = 0
for arch in self.arches:
if arch in breakarches: continue
diff = diff + (len(new[arch]) - len(old[arch]))
return diff <= 0
def eval_nuninst(self, nuninst):
res = []
total = 0
totalbreak = 0
for arch in self.arches:
if nuninst.has_key(arch):
n = len(nuninst[arch])
if arch in breakarches:
totalbreak = totalbreak + n
else:
total = total + n
res.append("%s-%d" % (arch[0], n))
return "%d+%d: %s" % (total, totalbreak, ":".join(res))
def slist_subtract(self, base, sub):
res = []
for x in base:
if x not in sub: res.append(x)
return res
def newlyuninst(self, nuold, nunew):
res = {}
for arch in self.arches:
if not nuold.has_key(arch) or not nunew.has_key(arch):
continue
res[arch] = \
self.slist_subtract(nunew[arch], nuold[arch])
return res
def eval_uninst(self, nuninst):
res = ""
for arch in self.arches:
if nuninst.has_key(arch) and nuninst[arch] != []:
res = res + " * %s: %s\n" % (arch,
", ".join(nuninst[arch]))
return res
def do_all(self, maxdepth = 0, init = []):
self.selected = []
self.selected_committed = 0
packages = self.packages[:]
earlyabort = 0
if maxdepth == "easy":
earlyabort = 1
maxdepth = 0
# meaningless to try forcing something _and_ recurse
force = 0
if maxdepth < 0:
force = 1
maxdepth = 0
earlyabort = 1
nuninst_start = self.get_nuninst()
if init:
self.writeout("leading: %s\n" % (",".join(init)))
for x in init:
if x not in packages:
self.writeout("failed: %s\n" % (x))
return None
y = packages.index(x)
self.selected.append(packages.pop(y))
for x in init:
self.doop_source(x)
if force:
self.nuninst_orig = self.get_nuninst()
else:
self.nuninst_orig = nuninst_start
self.writeout("start: %s\n" %
self.eval_nuninst(nuninst_start))
self.writeout("orig: %s\n" %
self.eval_nuninst(self.nuninst_orig))
if earlyabort:
nuninst_end = self.get_nuninst()
self.writeout("easy: %s\n" %
(self.eval_nuninst(nuninst_end)))
self.writeout(self.eval_uninst(
self.newlyuninst(self.nuninst_orig, nuninst_end)))
self.writeout("\n")
if not self.is_nuninst_asgood_generous(
self.nuninst_orig,
nuninst_end):
nuninst_end, respackages = None, None
else:
respackages = packages[:]
self.selected_committed = len(self.selected)
else:
nuninst_end, respackages = \
self.iter_some(maxdepth, packages, [])
if nuninst_end:
assert(len(self.selected) == self.selected_committed)
self.writeout("final: %s\n" %
",".join(self.selected))
self.writeout("start: %s\n" %
self.eval_nuninst(nuninst_start))
self.writeout(" orig: %s\n" %
self.eval_nuninst(self.nuninst_orig))
self.writeout(" end: %s\n" %
self.eval_nuninst(nuninst_end))
if force:
self.writeout("force breaks:\n")
self.writeout(self.eval_uninst(
self.newlyuninst(nuninst_start, nuninst_end)))
self.writeout("\n")
if not self.is_nuninst_asgood_generous(
self.nuninst_orig,
nuninst_end):
print "NON-None RETURN THAT'S NOT BETTER"
self.srcsn.commit_changes()
self.writeout("SUCCESS (%d/%d)\n" %
(len(self.packages), len(respackages)))
self.packages = respackages
self.sortpkgs()
return self.selected
else:
assert(len(self.selected) == len(init))
assert(self.selected_committed == 0)
for x in init:
self.srcsn.undo_change()
if self.srcsn.can_undo:
print "MORE OPS LEFT TO UNDO THAN DONE"
self.writeout("FAILED\n")
return None
def iter_end(self, available):
extra = []
count = 0
nuninst_comp = self.get_nuninst()
while available:
x = available.pop(0)
self.writeout("trying: %s\n" % (x))
self.doop_source(x)
if "/" in x:
better, nuninst_new = self.arch_improved_nuninst(
nuninst_comp, x[x.index("/")+1:])
else:
better, nuninst_new = self.get_improved_nuninst(
nuninst_comp)
if better:
self.selected.append(x)
count = count + 1
available.extend(extra)
extra = []
self.writeout("accepted: %s\n" % (x))
self.writeout(" ori: %s\n" %
(self.eval_nuninst(self.nuninst_orig)))
self.writeout(" pre: %s\n" %
(self.eval_nuninst(nuninst_comp)))
self.writeout(" now: %s\n" %
(self.eval_nuninst(nuninst_new)))
if len(self.selected) <= 20:
self.writeout(" all: %s\n" % (
" ".join(self.selected)))
else:
self.writeout(" most: (%d) .. %s\n" %
(len(self.selected),
" ".join(self.selected[-20:])))
nuninst_comp = nuninst_new
else:
self.writeout("skipped: %s (%d <- %d)\n" % (
x, len(extra), len(available)))
self.writeout(" got: %s\n%s" % (
self.eval_nuninst(nuninst_new),
self.eval_uninst(self.newlyuninst(
nuninst_comp, nuninst_new))))
self.srcsn.undo_change()
extra.append(x)
self.writeout(" finish: [%s]\n" %
",".join(self.selected[self.selected_committed:]))
self.writeout("endloop: %s\n" %
(self.eval_nuninst(self.nuninst_orig)))
self.writeout(" now: %s\n" %
(self.eval_nuninst(nuninst_comp)))
self.writeout(self.eval_uninst(
self.newlyuninst(self.nuninst_orig, nuninst_comp)))
self.writeout("\n")
if self.is_nuninst_asgood_generous(self.nuninst_orig,
nuninst_comp):
self.writeout("Apparently successful\n")
self.selected_committed = len(self.selected)
return (nuninst_comp, extra)
while count > 0:
self.srcsn.undo_change()
self.selected.pop()
count = count - 1
return (None, None)
def iter_some(self, depth, available, extra):
self.writeout("recur: [%s] %s %d/%d\n" % (
",".join(self.selected[:self.selected_committed]),
",".join(self.selected[self.selected_committed:]),
len(available), len(extra)))
if depth == 0:
extra.extend(available)
return self.iter_end(extra)
nuninst = None
while len(available) > depth:
x = available.pop(0)
if not skiphint(x):
self.doop_source(x)
self.selected.append(x)
res = self.iter_some(depth - 1, available[:], extra[:])
if res[0]:
nuninst = res[0]
available = filter(lambda x, y=res[1]: x in y,
available + extra)
# reset nuninst_orig too
self.nuninst_orig = nuninst
extra = []
continue
self.srcsn.undo_change()
self.selected.pop()
extra.append(x)
return (nuninst, extra)
# Package information
testing = britney.Sources(testingdir, arches)
testingpkgs = {}
for arch in arches:
testingpkgs[arch] = testing.Packages(arch)
testingbugs = read_bugs(testingdir + "/Bugs")
unstable = britney.Sources(unstabledir, arches)
unstablepkgs = {}
for arch in arches:
unstablepkgs[arch] = unstable.Packages(arch)
unstablebugs = read_bugs(unstabledir + "/Bugs")
unstabledates = read_dates(testingdir + "/Dates")
unstableurg = read_urgencies(testingdir + "/Urgency", testing, unstable)
testingupdates = britney.Sources(testingupdatesdir, arches)
testingupdatespkgs = {}
for arch in arches:
testingupdatespkgs[arch] = testingupdates.Packages(arch)
testingupdatesapproved = {} # pkg_ver -> who
for approver in ["ajt", "security-team", "ftpmaster", "cjwatson", "vorlon"]:
read_approvals(testingupdatesdir + "/Approved", approver,
testingupdatesapproved)
hlphints = ["easy", "hint", "remove", "block", "unblock", "approve"]
stdhints = ["easy", "hint", "remove", "block", "unblock", "urgent", "approve"]
allhints = ["force", "force-hint", "block-all"] + stdhints
hintsallowed = {
"ajt": allhints,
"rmurray": allhints,
"cjwatson": allhints,
"vorlon": allhints,
"aba": allhints,
"joeyh": stdhints + ["force"],
"djpig": stdhints,
"he": hlphints,
"adeodato": hlphints,
"ballombe": hlphints,
"luk": hlphints,
"freeze": ["block", "block-all"],
"ftpteam": ["block"]
}
hints = {"easy":[], "hint":[], "force-hint":[], "remove":[], "block":[], "block-all":[], "unblock":[], "force":[], "urgent":[], "approve":[]}
for who in hintsallowed.keys():
h = read_hints(unstabledir + "/Hints", who, hints, hintsallowed[who])
for k in hintsallowed[who]:
hints[k].extend(h[k])
for x in ["block", "block-all", "unblock", "force", "urgent", "remove"]:
z = {}
for a, b in hints[x]:
if z.has_key(a):
print "Overriding %s[%s] = %s with %s" % (x, a, z[a], b)
z[a] = b
hints[x] = z
for p, vh in hints["approve"]:
(v,h) = vh
testingupdatesapproved["%s_%s" % (p,v)] = h
hints["unblock"]["%s" % p] = (v,h)
def maxver(pkg, source, pkgs):
maxver = source.get_version(pkg)
for arch in arches:
pkgv = pkgs[arch].get_version(pkg)
if pkgv == None: continue
if maxver == None or britney.versioncmp(pkgv, maxver) > 0:
maxver = pkgv
return maxver
for pkg in testingbugs.keys() + unstablebugs.keys():
if not testingbugs.has_key(pkg): testingbugs[pkg] = 0
if not unstablebugs.has_key(pkg): unstablebugs[pkg] = 0
maxvert = maxver(pkg, testing, testingpkgs)
if maxvert == None:
testingbugs[pkg] = 0
continue
if testingbugs[pkg] == unstablebugs[pkg]: continue
maxveru = maxver(pkg, unstable, unstablepkgs)
if maxveru == None:
continue
if britney.versioncmp(maxvert, maxveru) >= 0:
testingbugs[pkg] = unstablebugs[pkg]
datenow = int(((time.time() / (60*60)) - 15) / 24);
# Next, work out which packages are candidates to be changed.
upgrademe = []
excuses = []
# Packages to be removed
for src in testing.sources:
if should_remove_source(src, testing, unstable, excuses):
upgrademe.append("-" + src)
# Packages to be upgraded from unstable:
for src in unstable.sources:
if testing.is_present(src):
tsrcv = testing.get_version(src) # silly optimisation
for arch in arches:
if should_upgrade_srcarch(src, arch, None, tsrcv,
testing, testingpkgs[arch],
unstable, unstablepkgs[arch],
excuses):
upgrademe.append("%s/%s" % (src, arch))
if should_upgrade_src(src, None, testing, testingpkgs,
unstable, unstablepkgs, None, excuses):
upgrademe.append(src)
for src in testingupdates.sources:
if testing.is_present(src):
tsrcv = testing.get_version(src) # silly optimisation
for arch in arches:
if should_upgrade_srcarch(src, arch, "tpu", tsrcv,
testing, testingpkgs[arch],
testingupdates,
testingupdatespkgs[arch],
excuses):
upgrademe.append("%s/%s_tpu" % (src, arch))
if should_upgrade_src(src, "tpu", testing, testingpkgs,
testingupdates, testingupdatespkgs,
testingupdatesapproved, excuses):
upgrademe.append("%s_tpu" % src)
for src in hints["remove"].keys():
if src in upgrademe: continue
if ("-"+src) in upgrademe: continue
if not testing.is_present(src): continue
tsrcv = testing.get_version(src)
if not same_source(tsrcv, hints["remove"][src][0]): continue
upgrademe.append("-%s" % (src))
exc = Excuse("-%s" % (src))
exc.set_vers(tsrcv, None)
exc.addhtml("Removal request by %s" % (hints["remove"][src][1]))
exc.addhtml("Package is broken, will try to remove")
excuses.append(exc)
def cmpexcuses(el, er):
return cmp(el.daysold, er.daysold) or cmp(el.name, er.name)
excuses.sort(cmpexcuses)
def reversed_exc_deps(excuses):
res = {}
for exc in excuses:
for d in exc.deps:
if not res.has_key(d): res[d] = []
res[d].append(exc.name)
return res
def invalidate(excuses, valid, invalid):
i = 0
exclookup = {}
for e in excuses:
exclookup[e.name] = e
revdeps = reversed_exc_deps(excuses)
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
unconsidered = []
for exc in excuses:
if exc.name not in upgrademe: unconsidered.append(exc.name)
for exc in excuses:
for d in exc.deps:
if d not in upgrademe and d not in unconsidered:
exc.addhtml("Unpossible dep: %s -> %s" % (exc.name, d))
invalidate(excuses, upgrademe, unconsidered)
f = open("update.EXCUSES_py", '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 exc in excuses:
f.write("<li>%s" % exc.html())
f.write("</ul></body></html>\n")
f.close()
del excuses
# Changes
srcsn = britney.SourcesNote(arches)
# Initialise new testing to be the old testing
for src in testing.sources:
srcsn.upgrade_source(testing, src)
srcsn.commit_changes()
#print "Things to do:"
#for x in upgrademe:
# print " " + x
run = UpgradeRun(srcsn, unstable, testingupdates, upgrademe)
def skiphint(candidate):
if "/" in candidate and candidate[candidate.rfind("/")+1:] in breakarches:
return 1
return 0
def do_hint(type, who, pkgvers):
hintinfo = {"easy": "easy",
"hint": 0,
"force-hint": -1,
}
hintdoall = hintinfo[type]
run.writeout("Trying %s from %s: %s\n" % (type, who,
" ".join( ["%s/%s" % (p,v) for (p,v) in pkgvers] )))
ok = 1
for xp,v in pkgvers:
# is this version of this package present in unstable?
# (if it's also present in testing, do_all will skip it)
if "/" in xp:
p = xp[:xp.find("/")]
else:
p = xp
if p[0] == "-":
pass
elif p.endswith("_tpu"):
if britney.versioncmp(run.testingupdates.get_version(p[:-4]),v) != 0:
ok = 0
run.writeout(" Version mismatch, %s %s != %s\n" %
(p, v, run.testingupdates.get_version(p[:-4])))
elif run.unstable.get_version(p) == None:
ok = 0
run.writeout(" Source %s has no version in unstable\n" % p)
elif britney.versioncmp(run.unstable.get_version(p), v) != 0:
ok = 0
run.writeout(" Version mismatch, %s %s != %s\n" %
(p, v, run.unstable.get_version(p)))
if ok:
run.do_all(hintdoall, map(lambda hp: hp[0], x[1]))
else:
run.writeout("Not using hint\n")
return ok
run.writeout("Generated on: %s\n" % (time.strftime("%Y.%m.%d %H:%M:%S %z", time.gmtime(time.time()))))
run.writeout("Arch order is: %s\n" % ", ".join(arches))
for x in hints["easy"]:
do_hint("easy", x[0], x[1])
for x in hints["force-hint"]:
do_hint("force-hint", x[0], x[1])
allpackages = []
normpackages = run.packages[:]
archpackages = {}
for a in breakarches:
la = len(a) + 1
archpackages[a] = [ p for p in normpackages if p[-la:] == "/" + a ]
normpackages = [ p for p in normpackages if p[-la:] != "/" + a ]
run.packages = normpackages
run.writeout("info: main run\n");
run.do_all()
allpackages += run.packages
for a in breakarches:
x = breakarches
breakarches = [ ba for ba in breakarches if ba != a ]
run.packages = archpackages[a]
run.writeout("info: broken arch run for %s\n" % (a))
run.do_all()
#run.do_all(1)
breakarches = x
allpackages += run.packages
run.packages = allpackages
#run.do_all(0,["caudium", "sablotron"])
hintcnt = 1
rand = whrandom.whrandom()
rand.seed(23,187,96)
for q in range(datenow):
rand.random()
q = rand.random()
q = 1.0
run.writeout("Current value is %f\n" % (q))
if q < 0.2:
q = 0.2
run.writeout("Current value bumped to %f\n" % (q))
maxloops = int(math.ceil(math.log(100/(q**0.5)) / math.log(1+len(run.packages))))
maxloops = 1
run.writeout("Max loops for q=%.2f is %d\n" % (q, maxloops))
for x in hints["hint"]:
if hintcnt > 50:
run.writeout("Skipping remaining hints...")
break
if len(x[1]) < maxloops:
run.writeout("Skipping simple hint from %s (%d<%d): %s\n"
% (x[0], len(x[1]), maxloops, str(x[1])))
continue
if do_hint("hint", x[0], x[1]):
hintcnt += 1
for i in range(1,maxloops):
run.do_all(i)
if maxloops <= 1 and len(run.packages) < 500:
# too many to do all of them, let's try 5 at random
num_at_random = 5
if len(run.packages) > num_at_random:
run.writeout("Trying %d at random\n" % num_at_random)
for k in range(num_at_random):
special = rand.choice(run.packages)
if skiphint(special): continue
run.writeout("Randomly trying %s\n" % (special))
run.do_all(0, [special])
run.srcsn.write_notes(testingdir)
write_bugs(testingdir + "/Bugs", testingbugs)
write_dates(testingdir + "/Dates", unstabledates)
f = open(testingdir + '/HeidiResult', 'w')
for arch in arches:
pkgs = srcsn.Packages(arch)
for pkg in pkgs.packages:
pkgv = pkgs.get_version(pkg)
pkgarch = pkgs.get_field(pkg, 'Architecture')
pkgsec = pkgs.get_field(pkg, 'Section')
if pkgsec == None: pkgsec = 'unknown\n'
pkgarch = pkgarch[:-1]
pkgsec = pkgsec[:-1]
f.write('%s %s %s %s\n' % (pkg, pkgv, pkgarch, pkgsec))
for src in srcsn.sources:
srcv = srcsn.get_version(src)
srcsec = srcsn.get_field(src, 'Section')
if srcsec == None: srcsec = 'unknown\n'
if srcsn.is_fake(src): srcsec = 'faux\n'
srcsec = srcsec[:-1]
f.write('%s %s source %s\n' % (src, srcv, srcsec))
f.close()
if len(arches) != expected_arches:
sys.exit(1)