view bin/publishdebianpackages.py @ 413:94a6ae627b31 treepkg-status

implemented CacheDb to store copied Packages from build host
author Bjoern Ricks <bricks@intevation.de>
date Fri, 23 Jul 2010 16:40:38 +0000
parents 58ecf7c0ecba
children e0539b483b04
line wrap: on
line source
#! /usr/bin/python
# Copyright (C) 2007 - 2010 by Intevation GmbH
# Authors:
# Bernhard Herzog <bh@intevation.de>
# Bjoern Ricks    <bjoern.ricks@intevation.de>
#
# This program is free software under the GPL (>=v2)
# Read the file COPYING coming with the software for details.

"""Publishes selected packages created by treepkg"""

import os
import sys
import shlex
import sqlite3

from optparse import OptionParser
from ConfigParser import SafeConfigParser

import treepkgcmd
from treepkg.readconfig import read_config_section, convert_bool
from treepkg.run import call, capture_output
from treepkg.cmdexpand import cmdexpand
from treepkg.publish import *
from treepkg.util import md5sum
from treepkg.info.status import TreepkgInfo

config_desc = ["distribution", "section", "num_newest",
               "build_user", "build_host", "build_listpackages",
               "publish_user", "publish_host",
               ("architectures", shlex.split, "armel i386 source"),
               ("after_upload_hook", shlex.split),
               ("publish_remove_old_packages", convert_bool),
               ("publish_dir", remove_trailing_slashes),
               ("cachedir",
                lambda s: expand_filename(remove_trailing_slashes(s)))]

class Package:
    
    def __init__(self, filename, trackname, packagename, packagepath, arch, md5sum):
        self.filename = filename
        self.trackname = trackname
        self.name = packagename
        self.sourcepath = packagepath
        self.arch = arch
        self.md5sum = md5sum


class CacheDb:


    def __init__(self, file):
        self.SELECT_PACKAGE_TMPL = """SELECT * FROM packages 
                                   WHERE filename = ?"""
        self.file = file
        self.conn = sqlite3.connect(file)
        self.cursor = self.conn.cursor()
        self.init_db(file)

    def init_db(self, file):
        self.cursor.execute(
               """CREATE TABLE IF NOT EXISTS packages (
                  filename TEXT PRIMARY KEY,
                  trackname TEXT,
                  packagename TEXT,
                  sourcepath TEXT,
                  arch TEXT, 
                  md5sum TEXT )""")
        self.conn.commit()


    def add_package(self, package):
        self.cursor.execute(self.SELECT_PACKAGE_TMPL, (package.filename,))
        row = self.cursor.fetchone()
        if not row:
            self.insert_package(package)
        else:
            self.update_package(package)

    def insert_package(self, package):
        INSERT_TMPL = """INSERT INTO packages VALUES (
                         ?, ?, ? ,? ,?, ?)"""
        self.cursor.execute(INSERT_TMPL, (package.filename,
                            package.trackname,
                            package.name,
                            package.sourcepath,
                            package.arch,
                            package.md5sum))
        self.conn.commit()

    def update_package(self, package):
        UPDATE_TMPL = """UPDATE packages set md5sum = '?' 
                         WHERE filename in (?)"""
        self.cursor.execute(UPDATE_TMPL, (package.md5sum, package.filename))
        self.conn.commit()

    def get_package(self, filename):
        self.cursor.execute(self.SELECT_PACKAGE_TMPL, (filename,))
        row = self.cursor.fetchone()
        if not row:
            return None
        return Package(row[0], row[1], row[2], row[3], row[4], row[5]) 

    def get_old_packages(self, newfiles):
        SELECT_TMPL = """SELECT * FROM packages 
                      WHERE filename not in (%s)"""
        tmp = ", ".join(['?'] * len(newfiles))
        self.cursor.execute(SELECT_TMPL % tmp, newfiles)
        return [Package(*row) for row in self.cursor.fetchall()]

    def remove_packages(self, files):
        DELET_TMPL = """DELETE FROM packages 
                     WHERE filename in (%s)"""
        tmp = ", ".join(['?'] * len(files))
        self.cursor.execute(DELET_TMPL % tmp, files)
        self.conn.commit()

    def get_packages(self):
        SELECT_TMPL = "SELECT * FROM packages" 
        self.cursor.execute(SELECT_TMPL)
        return [Package(*row) for row in self.cursor.fetchall()]

def read_config(filename):
    if not os.path.exists(filename):
        print >>sys.stderr, "Config file %s does not exist" % filename
        sys.exit(1)
    parser = SafeConfigParser()
    parser.read([filename])
    return read_config_section(parser, "publishpackages", config_desc)

def parse_commandline():
    parser = OptionParser()
    parser.set_defaults(config_file=os.path.join(treepkgcmd.topdir,
                                                 "publishpackages.cfg"),
                        quiet=False)
    parser.add_option("--config-file",
                      help=("The configuration file."
                            " Default is publishpackages.cfg"))
    parser.add_option("--dist",
                      help=("The debian distribution name to use on"
                            " the publishing system"))
    parser.add_option("--section",
                      help=("The debian distribution section name to use on"
                            " the publishing system"))
    parser.add_option("--track",
                      help=("The package track whose files are to be"
                            " published. If not given, files of all tracks"
                            " will be published"))
    parser.add_option("--quiet", action="store_true",
                      help=("Do not print progress meters or other"
                            " informational output"))
    return parser.parse_args()

def get_treepkg_info(variables):
    runremote = prefix_for_remote_command(variables["build_user"],
                                          variables["build_host"])
    xml = capture_output(cmdexpand("@runremote $build_listpackages"
                                     " --newest=$num_newest",
                                     runremote=runremote,
                                     **variables))
    return TreepkgInfo.fromxml(xml)

def get_binary_arch(arch): 
    if not arch is None and not arch.startswith("binary") and \
            not arch == "source":
            arch = "binary-" + arch
    return arch

def check_package_is_new(packagename, destdir, packagemd5sum):
    destpackage = os.path.join(destdir, packagename)
    if not os.path.isfile(destpackage):
        return True
    destmd5sum = md5sum(destpackage)
    return (destmd5sum != packagemd5sum)

def get_md5sum(packageinfo):
    md5sum = ""
    if packageinfo:
        for checksum in packageinfo.checksums:
            if checksum.type == "md5":
                md5sum = checksum.checksum
                break
    return md5sum

def sort_trackname_arch(a, b):
    if a.trackname < b.trackname: return -1
    if a.trackname > b.trackname: return +1
    return cmp(a.arch, b.arch)

def copy_files(destdir, files, quiet):
    scp_flags = []
    if quiet:
        scp_flags.append("-q")

    if not os.path.exists(destdir):
        os.makedirs(destdir)

    # scp the packages to the cache dir 
    call(cmdexpand("scp -p @scp_flags @files $cachedir/", files=files,
         scp_flags=scp_flags, cachedir=destdir))

def copy_to_destdir(dir, packages, quiet = False):
    packages.sort(cmp=sort_trackname_arch)
    package = packages[0]
    trackname = package.name
    arch = package.arch
    destdir = os.path.join(dir, arch, trackname)
    files = []
    for package in packages:
        if package.trackname != trackname or \
           package.arch != arch:
            #copy_files(destdir, files, quiet)
            trackname = package.trackname
            arch = package.arch
            destdir = os.path.join(dir, arch, trackname)
            files = []
        # add only if the packages differ
        if check_package_is_new(package.name, destdir, package.md5sum):
            files.append(package.sourcepath)
        print "package: %s, destdir: %s" % (package.name, destdir)

def copy_to_cache(variables, track, revision, quiet = False, architectures=None):
    cachedir = variables["cachedir"]
    newpackages = []
    treepkginfo = get_treepkg_info(variables)
    #allowedarchs = set([]) # contains all wanted architectures (incl. source)
    allarchs = set([]) # contains all present architectures (incl. source)
    binaryallpackages = []
    # change e.g. armel in binary-armel
    if not architectures is None:
        allowedarchs = set([get_binary_arch(a) for a in architectures])
    else:
        allowedarchs = set([])
    print "allowedarchs: %s" % allowedarchs
    for track in treepkginfo.tracks:
        for rev in track.revisions:
            for package in rev.packages:
                arch = get_binary_arch(package.arch)
                if package.type == "binary":
                    # skip other files
                    if package.arch is None:
                        continue
                    # handle binary-all
                    if arch == "binary-all":
                        # add trackname for subdir name
                        package.trackname = track.name
                        binaryallpackages.append(package)
                        continue
                    allarchs.add(arch)
                elif package.type == "source":
                    arch = package.type
                print "package: %s %s" % (package.name, arch)
                # only copy requested archs
                if len(allowedarchs) == 0 or \
                   arch in allowedarchs:
                    filename = os.path.join(cachedir, arch, track.name,
                                            package.name)
                    newpackage = Package(filename, track.name, package.name,
                                         package.path, arch, get_md5sum(package))
                    newpackages.append(newpackage)
    # copy binary-all packages
    sourcearch = set(["source"])
    if len(allowedarchs) == 0:
        binallarchs = allarchs - sourcearch 
    elif len(allarchs) == 0:
        binallarchs = allowedarchs - sourcearch
    else:
        binallarchs = (allowedarchs & allarchs) - sourcearch
    for package in binaryallpackages:
        for arch in binallarchs:
            filename = os.path.join(cachedir, arch, track.name,
                                    package.name)
            newpackage = Package(filename, package.trackname, package.name,
                                 package.path, arch, get_md5sum(package))
            newpackages.append(newpackage)
    print newpackages
    copy_to_destdir(cachedir, newpackages, quiet)

def publish_packages_arch(variables, track, revision, dist, section, 
                          quiet, architectures):
    copy_to_cache(variables, track, revision, quiet, architectures)
#    copy_to_publishdir(variables, dist, section, arch, quiet)

def publish_packages(config_filename, track, revision, dist, section, quiet):
    config = read_config(config_filename)

    if dist is None:
        dist = config["distribution"]
    if section is None:
        section = config["section"]

    architectures = config["architectures"]
    publish_packages_arch(config, track, revision, dist, section,
                          quiet, architectures)

    # update apt archive
#    call(config["after_upload_hook"])

def main():
    options, args = parse_commandline()
    revision = None # for future use cases
    publish_packages(options.config_file, options.track, revision,
                     options.dist, options.section, options.quiet)

if __name__ == "__main__":
    main()
This site is hosted by Intevation GmbH (Datenschutzerklärung und Impressum | Privacy Policy and Imprint)