#!/usr/bin/env python import sys, os, re, atexit, time, urllib, shutil import apt_pkg archive_root = 'http://archive.ubuntu.com/ubuntu' ports_root = 'http://ports.ubuntu.com/ubuntu' tempdir = None components = ('main', 'restricted', 'universe', 'multiverse') suites = ('dapper', 'hardy', 'intrepid', 'jaunty', 'karmic') # note that only 'LP: #1234' is official, but sometimes people get it wrong changelog_bug_pattern = re.compile('(?:lp:? ?#|href="/bugs/)([0-9]+)') #changelog_bug_pattern = re.compile('#([0-9]+)') published_date_pattern = re.compile('Published.*\n.*on ([-0-9]+)') def decompress_open(tagfile): if tagfile.startswith('http:') or tagfile.startswith('ftp:'): url = tagfile tagfile = urllib.urlretrieve(url)[0] atexit.register(os.unlink, tagfile) if tagfile.endswith('.gz'): import gzip import tempfile global tempdir if not tempdir: tempdir = tempfile.mkdtemp(prefix='suite-diff') atexit.register(shutil.rmtree, tempdir, True) decompressed = tempfile.mktemp(dir=tempdir) fin = gzip.GzipFile(filename=tagfile) fout = open(decompressed, 'wb') fout.write(fin.read()) fout.close() atexit.register(os.unlink, decompressed) return open(decompressed, 'r') else: return open(tagfile, 'r') def tagfiletodict(tagfile): suite = {} p = apt_pkg.ParseTagFile(decompress_open(tagfile)) while p.Step() == 1: suite[p.Section["Package"]] = p.Section["Version"] return suite def get_changelog_info(url): '''Parse LP per-version/per-release page URL and return tuple (date, bugs) with a publishing date (time record) and a bug list string.''' # enable this for quick testing #return (time.localtime(), '') chlog = urllib.urlopen(url).read() m = published_date_pattern.search(chlog) if m: date = time.strptime(m.group(1), '%Y-%m-%d') else: # less than a day ago (Soyuz uses "... hours ago") date = time.localtime() # cut out the actual changelog chlog = chlog[chlog.find('
')]
bugnums = set()
for m in changelog_bug_pattern.finditer(chlog):
bugnums.add(m.group(1))
bugs = ''
for bug in bugnums:
bugdata = urllib.urlopen('https://bugs.launchpad.net/bugs/%s' % bug).read()
if 'field.tag=verification-failed' in bugdata:
cls=' class="verificationfailed"'
elif 'field.tag=verification-done' in bugdata:
cls=' class="verified"'
else:
cls=''
if 'field.tag=hw-specific' in bugdata:
tag='(hw)'
else:
tag=''
bugs += '#%s%s ' % \
(bug, cls, bug, tag)
return (date, bugs)
def main():
apt_pkg.InitSystem()
pending = {} # component -> suite -> package -> (release_ver, proposed_ver, update_ver)
security_superseded = {} # component -> suite -> package -> (proposed_ver, security_ver)
cleanup = {} # suite -> [(package, version)]
for suite in suites:
for component in components:
release = tagfiletodict('%s/dists/%s/%s/source/Sources.gz' %
(archive_root, suite, component))
proposed = tagfiletodict('%s/dists/%s-proposed/%s/source/Sources.gz' %
(archive_root, suite, component))
updates = tagfiletodict('%s/dists/%s-updates/%s/source/Sources.gz' %
(archive_root, suite, component))
security = tagfiletodict('%s/dists/%s-security/%s/source/Sources.gz' %
(archive_root, suite, component))
for package in sorted(proposed.keys()):
update_ver = updates.get(package, '')
if apt_pkg.VersionCompare(proposed[package], update_ver) > 0:
pending.setdefault(component, {}).setdefault(
suite, {})[package] = (release.get(package, ''),
proposed[package], update_ver)
security_ver = security.get(package, '')
if apt_pkg.VersionCompare(proposed[package], security_ver) < 0:
security_superseded.setdefault(component, {}).setdefault(
suite, {})[package] = (proposed[package], security_ver)
else:
cleanup.setdefault(suite, []).append((package, proposed[package]))
# a:link { color: blue; background: #CCCCB0; }
# a:visited { color: blue; background: #CCCCB0; }
# a:hover { color: red; background: #CCCCB0; }
print '''
Pending Ubuntu SRUs
Pending Ubuntu stable release updates
'''
print 'Generated: %s by sru-report
' % time.strftime('%x %X UTC', time.gmtime())
print 'Jump to:',
for component in components:
print '%s' % (component, component),
print 'security-superseded cleanup
'
print '''A stable
release update is currently in progress for the following packages, i. e.
they have a newer version in -proposed than in -updates.
Bugs in green are verified by QA, bugs in red failed verification.
'''
for component in components:
if not component in pending:
continue
print '%s
' % (component, component)
for suite in suites:
if not suite in pending[component]:
continue
print '''%s
| Package | -release | -updates | -proposed | changelog bugs | days |
|---|---|---|---|---|---|
| %s | \%s | \%s | \%s | \%s | %i |
The following SRUs have been shadowed by a security update and need to be re-merged:
' for component in components: if not component in security_superseded: continue print '| Package | -proposed | -security |
|---|---|---|
| %s | \%s | \%s |
The following packages have an equal or higher version in -updates and should be removed from -proposed:
' for suite in suites: if suite not in cleanup: continue print '' % suite
for (pkg, ver) in cleanup[suite]:
print 'lp-remove-package.py -y -u $SUDO_USER -m "moved to -updates" -s %s-proposed -e %s %s' % (
suite, ver, pkg)
print ''
print '''
'''
if __name__ == '__main__':
main()