#!/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 = "%s (%s to %s)\n
\n" % \
(self.name, self.name, self.name, self.ver[0], self.ver[1])
if self.maint:
res = res + "
Maintainer: %s\n" % (self.maint)
if self.section and string.find(self.section, "/") > -1:
res = res + "
Section: %s\n" % (self.section)
if self.daysold != None:
if self.daysold < self.mindays:
res = res + ("
Too young, only %d of %d days old\n" %
(self.daysold, self.mindays))
else:
res = res + ("
%d days old (needed %d days)\n" %
(self.daysold, self.mindays))
for x in self.htmlline:
res = res + "
" + x + "\n"
for x in self.deps:
if x in self.invalid_deps:
res = res + "
Depends: %s %s (not considered)\n" % (self.name, x, x)
else:
res = res + "
Depends: %s %s\n" % (self.name, x, x)
for (n,a) in self.break_deps:
if n not in self.deps:
res += "
Ignoring %s depends: %s\n" % (a, n, n)
res = res + "
\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 %s)" % \
(", ".join(oodbins[v]), arch, src, v, v)
text = "out of date on %s: %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 buggy! (%d > %d)" % \
(pkg, ", ".join(pkgs[pkg]), pkg,
unstablebugs[pkg], testingbugs[pkg]))
updatecand = 0
elif unstablebugs[pkg] > 0:
exc.addhtml("%s (%s) is (less) buggy! (%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 = "-