[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Re: [proposal] Documenting which revision is installed



On Fri, Dec 28, 2007 at 08:20:01AM +0100, Frans Pop wrote:
> I agree with Anthony that it is very much preferable to have a solution
> that's just able to automatically determine the correct version.

Okay, so here's a updated version that generates both debian_version and
lsb-release as specced by dato, if it's given arguments of debian-version
or dato-release, and has appropriate perms.

In order for it to be useful, I guess it'd need to be in base, and hence
written in perl or C (unless we decided to add python to base).

] $ ./debian_version.py debian-version
] /etc/debian_version should contain:
] lenny
] $ ./debian_version.py dato-release
] /etc/lsb-release should contain:
] DISTRIB_ID=Debian
] DISTRIB_RELEASE=lenny
] DISTRIB_CODENAME=lenny
] DISTRIB_DESCRIPTION="Debian x.y Testing distribution - Not Released"

] $ ./debian_version.py noisy
] Packages seem to be from Debian release lenny
] Debian x.y Testing distribution - Not Released
] ['mirror.internode.on.net_pub_debian_dists_lenny_Release', 'http.us.debian.org_debian_dists_lenny_Release']
]      89.4% HIT RATE (1293 of 1447)
]      95.3% of installed packages available
]       4.4% of matching packages could be upgraded
]       1.8% of matching packages are more recent

] $ sudo rm /etc/debian_version /etc/lsb-release
] $ sudo ./debian_version.py debian-version dato-release
] $ cat /etc/debian_version; echo ----; cat /etc/lsb-release
] lenny
] ----
] DISTRIB_ID=Debian
] DISTRIB_RELEASE=lenny
] DISTRIB_CODENAME=lenny
] DISTRIB_DESCRIPTION="Debian x.y Testing distribution - Not Released"

Cheers,
aj

#!/usr/bin/env python

# Copyright 2007 Anthony Towns
# GPL v2 or later.

import apt_pkg, sys, os

apt_pkg.init()

def try_write(file):
    try:
        f = open(file, "w")
    except IOError:
        f = sys.stdout
        f.write("%s should contain:\n" % file)
    return f

def uniq(l):
    return dict( [(x,1) for x in l] ).keys()

def get_installed():
    vs = {}
    i = open("/var/lib/dpkg/status", "r")
    p,v,x = None,None,None
    for l in i.xreadlines():
	l = l.rstrip()
	if l.startswith("Package: "): p = l[l.find(" ")+1:]
	if l.startswith("Version: "): v = l[l.find(" ")+1:]
	if l.startswith("Status: "):
	    x = l.split(" ")[1:]
	if l == "":
	    if x and x[0] != "hold" and x[1] == "ok" and x[2] == "installed":
		vs[p] = v
	    p,v,x = None,None,None
    i.close()
    return vs

vlal = "/var/lib/apt/lists"
arch = "".join(os.popen("dpkg --print-architecture", "r").readlines()).rstrip()

def releases():
    urs = {}
    for path in os.listdir(vlal):
        if not path.endswith("_Release.gpg"): continue
        path = path[:-4]

	release_info = []
	o,l,s,v,c = [None] * 5
	for l in open(os.path.join(vlal,path)).xreadlines():
	    l = l.rstrip()
	    if l[0] != " ":
		release_info.append(
			(l[:l.find(":")], l[l.find(":")+1:].lstrip()) )
	    else:
		x = release_info[-1]
		x = (x[0], x[1] + "\n" + l[1:])
		release_info[-1] = x
	release_info_l = release_info
	release_info = dict(release_info)

	vendor = filter(None, [
		release_info.get("Label", None),
		release_info.get("Origin", None),
		"unknown"] ) [0]

	version = filter(None, [
		release_info.get("Version", None),
		release_info.get("Codename", None),
		release_info.get("Suite", None),
		"unknown"] ) [0]

	pkgs = [x for x in os.listdir(vlal) 
			if x.startswith(path[:-7])
			    and x.endswith("_binary-%s_Packages" % arch) ]

	k = (vendor,version)
	if k not in urs: urs[k] = ([],[],[])
	urs[k][0].append( path )
	urs[k][1].append( release_info )
	urs[k][2].extend( pkgs )

    res = []
    for k,v in urs.iteritems():
	(vendor, version) = k
        (paths, rels, pkgs) = v
	res.append( (vendor, version, paths, rels, readlist(pkgs)) )

    return res

def readlist(files):
    vs = {}
    for file in files:
        i = open(os.path.join(vlal, file), "r")
        p,v = None,None
        for l in i.xreadlines():
  	    l = l.rstrip()
	    if l.startswith("Package: "): p = l[l.find(" ")+1:]
	    if l.startswith("Version: "): v = l[l.find(" ")+1:]
	    if l == "":
	        vs[p] = v
	        p,v = None,None
        i.close()
    return vs.items()

def compare(installed, list):
    same, new, old = 0,0,0
    tryagain = dict(installed)
    for (p, tv) in list:
        if p in installed:
            if tv == installed[p]:
	        same += 1
		del tryagain[p]
	    else:
                x = apt_pkg.VersionCompare(installed[p], tv)
	        if x > 0:
		    old += 1
	        elif x < 0:
		    new += 1
	        else:
		    same += 1
		    del tryagain[p]
    return same,new,old,tryagain

installed = get_installed()
release_pkgs = releases()

noisy = ("noisy" in sys.argv[1:])
iterate = ("iterate" in sys.argv[1:])
debian_version = ("debian-version" in sys.argv[1:])
dato_release = ("dato-release" in sys.argv[1:])

intro = "Packages"
while installed:
    best = 0,len(installed),0,"-","-",[],"-",[]
    for vendor, version, path, rels, packages in release_pkgs:
        same,new,old,tryagain = compare(installed, packages)
        if same > best[0]:
	    best = same,new,old,vendor,version,rels,path,tryagain

    same,new,old,vendor,version,rels,path,tryagain = best
    if same == 0: break

    total = same+new+old
    inst = len(installed)

    if noisy:
        print "%s seem to be from %s release %s" % (intro, vendor, version)
        print "%s" % (rels[0].get("Description","(no description)"))
        print "%s" % (path)
        print "    %5.1f%% HIT RATE (%d of %d)" % (100.0*same/inst, same, inst)
        print "    %5.1f%% of installed packages available" % (100.0*total/inst)
        print "    %5.1f%% of matching packages could be upgraded" % (100.0*new/total)
        print "    %5.1f%% of matching packages are more recent" % (100.0*old/total)

    if debian_version:
        debian_version = 0
	f = try_write("/etc/debian_version")
	f.write("%s\n" % (version))
	del f
    if dato_release:
        dato_release = 0
	f = try_write("/etc/lsb-release")
        f.write("DISTRIB_ID=%s\n" % (vendor))
        f.write("DISTRIB_RELEASE=%s\n" % (version))
	for c in uniq([ x.get("Codename", None) for x in rels ]):
	    if c == None: continue
            f.write("DISTRIB_CODENAME=%s\n" % (c))
	    break
	for d in uniq([ x.get("Description", None) for x in rels ]):
	    if d == None: continue
            f.write("DISTRIB_DESCRIPTION=\"%s\"\n" % (d))
            break

    if iterate and noisy:
        installed = tryagain
        intro = "\nRemaining packages"
    else:
        installed = []

if installed and iterate and noisy:
    print "\nRemaining packages:"
    for p,v in installed.iteritems():
        print "    %s: %s" % (p,v)

Attachment: signature.asc
Description: Digital signature


Reply to: