Logo Search packages:      
Sourcecode: falcon version File versions  Download package

app_install.py

# This file is part of the Falcon repository manager
# Copyright (C) 2005-2008 Dennis Kaarsemaker
# See the file named COPYING in the root of the source tree for license details
#
# app_install.py - Extract data for app_install and build packages from it

import falcon
from gettext import gettext as _
import time, os, re, tarfile, socket, shutil

icon_re =  re.compile(r'.*?^icon *?= *?([^\n]*?)\n.*', re.DOTALL | re.I | re.M)
version_re = re.compile(r'.*?^version:[^\n]*-(\d+)\n.*', re.DOTALL | re.I | re.M)

00014 class AppInstallDataPlugin(falcon.plugin.FalconPlugin):
    """AppInstall extractor/package creator"""
    name = _("app-install data")
    desc = _("Generate app-install data for your repository")
    api  = (1,1)

    cache = os.path.join('.falcon','app-install')

    def __init__(self):
        self.conf.register('needs_update', [], None)
        self.conf.register('include_self', True, falcon.questions.Boolean(_("Include the main server as mirror")))
        self.conf.register('components', '', falcon.questions.Entry(_("Which components should be included in the generated sources.list")))
        self.origin = falcon.conf.origin.lower()

    def post_action(self, action, args):
        if action == 'clean':
            # Clean out our cache
            if not os.path.exists(cache):
                return
            for pocket in os.listdir(cache):
                if not os.path.exists(os.path.join(cache,pocket)):
                    continue
                for package in os.listdir(os.path.join(cache,pocket)):
                    try:
                        falcon.package.Package.get(packagename=package, component__pocket__name=pocket)
                    except falcon.package.Package.DoesNotExist:
                        shutil.rmtree(os.path.join(self.cache, pocket, package))
                        if pocket not in self.conf.needs_update:
                            self.conf.needs_update = self.conf.needs_update + [pocket.name]

    def post_export(self, result, pocket):
        if type(pocket) != falcon.pocket.Pocket:
            return
        # Create our .deb
        src = os.path.join(falcon.conf.prefix, 'share', 'falcon', 'app-install-data')
        mirrors = ' '.join(m.webbase for m in falcon.mirror.Mirror.objects.all())
        if self.conf.include_self:
            mirrors += ' ' + falcon.conf.webbase

        if pocket.name in self.conf.needs_update:
            falcon.util.output(_("Updating app-install-data package"))
            p = pocket
            dst = os.path.join('.falcon','app-install',p.name,'app-install-data-'+self.origin)
            for f in falcon.mirror.listdir(src):
                if os.path.isdir(os.path.join(src,f)):
                    continue
                falcon.util.writefile(os.path.join(dst, f.replace('ORIGIN', self.origin)),
                falcon.util.readfile(os.path.join(src,f)).replace('ORIGIN', self.origin).\
                                                          replace('GPGKEY', falcon.conf.gpgkey).\
                                                          replace('MIRRORS', mirrors))
            # Changelog!
            cl = os.path.join(dst, 'debian', 'changelog')
            if not os.path.exists(cl):
                os.environ['DEBFULLNAME'] = "Falcon %s" % falcon.conf.falcon_version
                os.environ['DEBEMAIL'] = 'falcon@%s' % socket.gethostname()
                os.environ['EDITOR'] = ':'
                falcon.util.run(['dch', '--no-conf', '-D', p.name, '--create', '--package', 'app-install-data-' + self.origin,
                                 '-v', '%s-1' % p.version, '-p', '-c', cl, '--distributor', self.origin])
            else:
                os.environ['DEBFULLNAME'] = "Falcon %s" % falcon.conf.falcon_version
                os.environ['DEBEMAIL'] = 'falcon@%s' % socket.gethostname()
                changes = falcon.util.run(['dpkg-parsechangelog', '-l' + cl])
                v = int(version_re.sub(r'\1', changes)) + 1
                falcon.util.run(['dch', '--no-conf', '-D', p.name, '-v', '%s-%d' % (p.version,v), '-p', '-c', cl,
                                 '--distributor', self.origin, _("Automatic update")])
            # Write key/list
            if falcon.conf.gpgkey:
                if not os.path.exists(os.path.join(dst, '%s.key' % self.origin)):
                    falcon.util.run(['gpg', '--export', '--armor', falcon.conf.gpgkey], 
                                    outfile=os.path.join(dst, '%s.key' % self.origin))
            falcon.util.writefile(os.path.join(dst, '%s.list' % self.origin),
                                  'deb MIRROR %s %s\ndeb-src MIRROR %s %s' % (p.name, self.conf.components, p.name, self.conf.components))
            os.chmod(os.path.join(dst,'debian','rules'), 0755)
            falcon.util.run(['dpkg-buildpackage', '-rfakeroot', '-us', '-uc'], wd=dst, buffer=not falcon.conf.verbose)

        # Copy over and replace APPINSTALLDATA with the proper URL
        src = os.path.join(falcon.conf.rootdir, '.falcon', 'app-install', pocket.name)
        dst = os.path.join(falcon.conf.rootdir, 'dists', pocket.name)
        debs = sorted([x for x in os.listdir(src) if x.endswith('deb')])
        if len(debs):
            file = debs[-1]
            if not os.path.exists(os.path.join(dst, file)):
                shutil.copy(os.path.join(src, file), dst)
            falcon.util.writefile(os.path.join(dst, 'index.html'), 
                                  falcon.util.readfile(os.path.join(dst, 'index.html')).replace('APPINSTALLDATA', file))

        self.conf.needs_update = [x for x in self.conf.needs_update if x != pocket.name]

    def pre_rsync(self, mirror):
        # Copy over and replace APPINSTALLDATA with the proper URL
        for pocket in falcon.pockets:
            src = os.path.join(falcon.conf.rootdir, '.falcon', 'app-install', pocket.name)
            dst = os.path.join(falcon.conf.rootdir, '.falcon', 'base-%s' % mirror.name, 'dists', pocket.name)
            debs = sorted([x for x in os.listdir(src) if x.endswith('deb')])
            if len(debs):
                file = debs[-1]
                if not os.path.exists(os.path.join(dst, file)):
                    shutil.copy(os.path.join(src, file), dst)
                falcon.util.writefile(os.path.join(dst, 'index.html'), 
                                      falcon.util.readfile(os.path.join(dst, 'index.html')).replace('APPINSTALLDATA', file))

    def post_install(self, result, component, package):
        if result != package:
            return
        # Don't recursively extract the generated .deb
        if package.packagename.startswith('app-install-data'):
            return
        # Only update on new upstream versions
        upstream_version = package.version[package.version.find(':')+1:]
        if '-' in upstream_version:
            upstream_version = upstream_version[:upstream_version.rfind('-')]
        pcache = os.path.join(self.cache, component.pocket.name, 'app-install-data-' + self.origin, package.packagename, upstream_version)
        if os.path.exists(pcache):
            return
        # Clean up old versions
        if os.path.exists(os.path.dirname(pcache)):
            for d in os.listdir(os.path.dirname(pcache)):
                shutil.rmtree(os.path.join(os.path.dirname(pcache), d))

        # Only update if the package has desktopfiles
        desktopfiles = {}
        seen_files = False
        for b in package.binaries.distinct('packagename'):
            # Assume packages have the same files in all architectures, so
            # overwriting things in desktopfils is ok
            desktopfiles[b] = [x for x in b.files if x.endswith('.desktop')]
            seen_files |= len(desktopfiles[b])
        if seen_files:
            if component.pocket.name not in self.conf.needs_update:
                self.conf.needs_update = self.conf.needs_update + [component.pocket.name]
            # Extract all .desktop files and the .png files they reference
            for b in desktopfiles:
                falcon.util.debug(_("Processing %s") % b.filename)
                fd = open(os.path.join(package.component.poolpath, b.filename))
                pos = 8
                fd.seek(pos + 48)
                size = int(fd.read(10).strip())
                pos += 60 + size
                fd.seek(pos + 48)
                size = int(fd.read(10).strip())
                pos += 60 + size
                fd.seek(pos)
                fn = fd.read(16).strip()
                fd.seek(pos+60)

                # Wrap the fd to make tarfile work
                c = fd.read(1)
                if c == '\n':
                    fd = FileWrapper(fd, pos+61)
                else:
                    fd.seek(pos+60)
                    fd = FileWrapper(fd, pos+60)
                
                if fn.endswith('.gz'):
                    tf = tarfile.open(name='', mode='r:gz', fileobj=fd)
                elif fn.endswith('.bz2'):
                    tf = tarfile.open(name='', mode='r:bz2', fileobj=fd)

                for f in desktopfiles[b]:
                    df = tf.extractfile(f).read().strip()
                    df += '\nX-AppInstall-Channel=%s\nX-AppInstall-Package=%s' % (self.origin, b.packagename)
                    falcon.util.writefile(os.path.join(pcache, os.path.basename(f)),df)
                    # Find icon
                    icon = icon_re.sub(r'\1', df)
                    if icon and not os.path.exists(os.path.join(pcache, os.path.basename(icon))):
                        if '/' not in icon and os.path.join('usr','share','pixmaps',icon) in b.files:
                            falcon.util.writefile(os.path.join(pcache, icon), tf.extractfile(os.path.join('usr','share','pixmaps',icon)).read())
                        else:
                            # Silly package
                            if icon[0] == '/': icon = icon[1:]
                            for f in b.files:
                                if f.endswith(icon):
                                    falcon.util.writefile(os.path.join(pcache, os.path.basename(f)), tf.extractfile(f).read())

# File wrapping object for files with an offset
00189 class FileWrapper:
    """Wrap a file object and read/write with an offset"""
    def __init__(self, fd, pos):
        self.fd = fd
        self.pos = pos 

    def read(self, nbytes):
        return self.fd.read(nbytes)

    def seek(self, offset, whence=0):
        if whence == 0:
            offset += self.pos
        return self.fd.seek(offset, whence)

    def tell(self):
        return self.fd.tell() - self.pos

# Utility func to generate the cache, useful to call from within a falcon shell
def generate_cache():
    """Scan all installed packages for app_install data and extract it"""
    pi = None
    for pi in falcon.plugin.enabled_plugins:
        if isinstance(pi, AppInstallDataPlugin):
            break
    else:
        falcon.util.warning(_("Plugin not enabled -- can't generate cache"))
    for package in falcon.package.SourcePackage.objects.all():
        falcon.util.debug(_("Processing %s %s") % (package.packagename, package.version))
        pi.post_install(package, package.component, package)

Generated by  Doxygen 1.6.0   Back to index