changeset 84:98a7d70746a9

Make BinaryPackager remove all files that do not belong to the binary package from the pkg/<rev>/binary directory after pbuilder finished. Also, add tests for this and some corresponding test support code.
author Bernhard Herzog <bh@intevation.de>
date Mon, 10 Sep 2007 17:13:33 +0000 (2007-09-10)
parents 83e48a76f759
children 31b0567df051
files test/filesupport.py test/mocksudopbuilder.py test/test_packager.py treepkg/packager.py
diffstat 4 files changed, 379 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/filesupport.py	Mon Sep 10 17:13:33 2007 +0000
@@ -0,0 +1,92 @@
+# Copyright (C) 2007 by Intevation GmbH
+# Authors:
+# Bernhard Herzog <bh@intevation.de>
+#
+# This program is free software under the GPL (>=v2)
+# Read the file COPYING coming with the software for details.
+
+"""Support code for the test cases"""
+
+import os
+import shutil
+from treepkg.util import writefile
+
+
+def create_temp_dir():
+    """Create a temporary directory for the test-suite and return its name.
+
+    The temporary directory is always called temp and is created in the
+    directory where the support module is located.
+
+    If the temp directory already exists, just return the name.
+    """
+    name = os.path.abspath(os.path.join(os.path.dirname(__file__), "temp"))
+
+    # if the directory already exists, we're done
+    if os.path.isdir(name):
+        return name
+
+    # create the directory
+    os.mkdir(name)
+    return name
+
+
+class FileTestMixin:
+
+    """Mixin class for tests that use files in the temporary directory
+    """
+
+    def temp_file_name(self, basename, remove=False):
+        """Returns the full name of the file named basename in the temp. dir.
+        If the remove parameter is true, the file is removed if already exists.
+        """
+        filename = os.path.join(create_temp_dir(), basename)
+        if remove and os.path.exists(filename):
+            os.remove(filename)
+        return filename
+
+    def create_temp_file(self, basename, contents, mode = None):
+        """Creates a file in the temp directory with the given contents.
+        The optional parameter mode should either be None (the default)
+        or an int specifying the file permissions (same format as the
+        second parameter of os.chmod).  The method returns the absolute
+        name of the created file.
+        """
+        filename = self.temp_file_name(basename)
+        file = open(filename, "w")
+        file.write(contents)
+        file.close()
+        if mode is not None:
+            os.chmod(filename, mode)
+        return filename
+
+    def create_temp_dir(self, basename, remove=True):
+        """Creates the directory basename in the temporary directory.
+        If the optional parameter remove is true (the default), the
+        directory and all its contents are deleted with shutil.rmtree.
+        The method returns the absolute name of the created directory.
+        """
+        dirname = os.path.join(create_temp_dir(), basename)
+        if remove and os.path.exists(dirname):
+            shutil.rmtree(dirname)
+        os.mkdir(dirname)
+        return dirname
+
+    def create_files(self, directory, filedesc):
+        """Creates a hieararchy of directories and files in directory.
+        The filedesc parameter should be a sequence of (name, contents)
+        pairs.  Each pair describes one entry of the directory.  If
+        contents is an instance of list, the entry is a subdirectory and
+        the contents is a list in the same format as filedesc and passed
+        recursively to the create_files method.  If contents is a
+        string, the new directory entry is a normal file and contents is
+        the contents of the file.
+        """
+        for name, contents in filedesc:
+            if isinstance(contents, list):
+                # a list as contents indicates a directory
+                newdir = os.path.join(directory, name)
+                os.mkdir(newdir)
+                self.create_files(newdir, contents)
+            else:
+                writefile(os.path.join(directory, name), contents)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/mocksudopbuilder.py	Mon Sep 10 17:13:33 2007 +0000
@@ -0,0 +1,150 @@
+#! /usr/bin/env python
+# Copyright (C) 2007 by Intevation GmbH
+# Authors:
+# Bernhard Herzog <bh@intevation.de>
+#
+# This program is free software under the GPL (>=v2)
+# Read the file COPYING coming with the software for details.
+
+"""
+Mock sudo that mimics some aspects of sudo pbuilder invocations for test cases
+"""
+
+import sys
+import os
+import shutil
+import tempfile
+import re
+
+from optparse import OptionParser
+
+sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir))
+
+from treepkg.util import extract_value_for_key, writefile
+from treepkg.run import call
+from treepkg.cmdexpand import cmdexpand
+
+
+def parse_command_line(args):
+    if len(args) < 2:
+        raise RuntimeError("mocksudopbuilder must be called with at least"
+                           " two parameters")
+    if args[0] != "/usr/sbin/pbuilder":
+        raise RuntimeError("mocksudopbuilder must be called with"
+                           " /usr/sbin/pbuilder as first argument")
+
+    cmd = args[1]
+    if cmd != "build":
+        raise RuntimeError("mocksudopbuilder only supports the pbuilder build"
+                           " command")
+
+    args = args[2:]
+
+    parser = OptionParser()
+    parser.add_option("--configfile")
+    parser.add_option("--logfile")
+    parser.add_option("--buildresult")
+
+    opts, rest = parser.parse_args(args)
+    return opts, rest
+
+
+class DSC(object):
+
+    def __init__(self, filename):
+        self.filename = os.path.abspath(filename)
+        self.parse_dsc_file()
+
+    def parse_dsc_file(self):
+        lines = open(self.filename).readlines()
+
+        self.source_package = extract_value_for_key(lines, "Source:")
+
+        raw_binaries = extract_value_for_key(lines, "Binary:")
+        self.binaries =  [name.strip() for name in raw_binaries.split(",")]
+
+        version = extract_value_for_key(lines, "Version:")
+        match = re.match(r"\d+:", version)
+        if match:
+            version = version[match.end(0):]
+        self.version = version
+        match = re.search("-([^-]+)$", version)
+        if match:
+            self.base_version = version[:match.start(0)]
+            self.revision = match.group(1)
+        else:
+            self.base_version = version
+            self.revision = ""
+
+        self.files = []
+        for i in range(len(lines) - 1, -1, -1):
+            line = lines[i]
+            if line[0] != " ":
+                break
+            self.files.append(line.split()[-1])
+
+def unpack_source_pkg(dsc):
+    tempdir = tempfile.mkdtemp()
+    call(cmdexpand("dpkg-source -x $filename", filename=dsc.filename),
+         cwd=tempdir, stdout=sys.stdout)
+    return os.path.join(tempdir, dsc.source_package + "-" + dsc.base_version)
+
+def parse_debian_control(filename):
+    packages = []
+    lines = [line.strip() for line in open(filename).readlines()]
+    while lines:
+        try:
+            empty_line = lines.index("")
+        except ValueError:
+            empty_line = len(lines)
+        paragraph = lines[:empty_line]
+        lines = lines[empty_line + 1:]
+
+        package_name = extract_value_for_key(paragraph, "Package:")
+        arch = extract_value_for_key(paragraph, "Architecture:")
+        if package_name is not None:
+            packages.append((package_name, arch))
+    return packages
+
+def create_binary_results(buildresult, dsc, packages):
+    for name, arch in packages:
+        if arch == "any":
+            arch = "i386"
+        writefile(os.path.join(buildresult,
+                               "%s_%s_%s.deb" % (name, dsc.version, arch)),
+                  "")
+
+    basename = os.path.splitext(os.path.basename(dsc.filename))[0]
+    writefile(os.path.join(buildresult, basename + "_i386.changes"), "")
+
+def create_source_results(buildresult, dsc):
+    dscdir = os.path.dirname(dsc.filename)
+    shutil.copy(dsc.filename, buildresult)
+    for name in dsc.files:
+        shutil.copy(os.path.join(dscdir, name), buildresult)
+
+def mock_pbuilder_build(opts, dscfilename):
+    dsc = DSC(dscfilename)
+    print "unpacking source package"
+    unpacked_dir = unpack_source_pkg(dsc)
+    try:
+        packages = parse_debian_control(os.path.join(unpacked_dir, "debian",
+                                                     "control"))
+        create_binary_results(opts.buildresult, dsc, packages)
+        create_source_results(opts.buildresult, dsc)
+    finally:
+        print "removing tempdir", repr(os.path.dirname(unpacked_dir))
+        shutil.rmtree(os.path.dirname(unpacked_dir))
+
+def main():
+    opts, rest = parse_command_line(sys.argv[1:])
+    for optname in ["buildresult"]:
+        if getattr(opts, optname) is None:
+            raise RuntimeError("Missing required option %r" % optname)
+    if len(rest) != 1:
+        raise RuntimeError("Missing required argument for .dsc file")
+    if opts.logfile:
+        sys.stdout = open(opts.logfile, "w", 1)
+    mock_pbuilder_build(opts, rest[0])
+
+main()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/test_packager.py	Mon Sep 10 17:13:33 2007 +0000
@@ -0,0 +1,132 @@
+# Copyright (C) 2007 by Intevation GmbH
+# Authors:
+# Bernhard Herzog <bh@intevation.de>
+#
+# This program is free software under the GPL (>=v2)
+# Read the file COPYING coming with the software for details.
+
+"""Tests for treepkg.packager"""
+
+import sys
+import os
+import unittest
+
+from treepkg.run import call
+from treepkg.cmdexpand import cmdexpand
+from treepkg.util import writefile
+from treepkg.packager import PackagerGroup
+import treepkg.subversion as subversion
+import treepkg
+
+from filesupport import FileTestMixin
+
+
+def create_svn_repository(directory):
+    baseurl = "file://" + directory
+    call(cmdexpand("svnadmin create --fs-type fsfs $directory",
+                   **locals()))
+    call(cmdexpand("svn mkdir -q -m 'create directory structure'"
+                   " $baseurl/trunk",
+                   **locals()))
+    return baseurl
+
+def add_svn_files(workingcopy, filedesc, commitmsg):
+    for name, contents in filedesc:
+        writefile(os.path.join(workingcopy, name), contents)
+        call(cmdexpand("svn add -q $name", **locals()),
+             cwd=workingcopy)
+    call(cmdexpand("svn commit -q -m $commitmsg", **locals()),
+         cwd=workingcopy)
+
+
+class SourcePackager(treepkg.packager.SourcePackager):
+
+    pkg_basename = "testpkg"
+
+    def do_package(self):
+        pkgbaseversion, pkgbasedir = self.export_sources()
+
+        pkgbasename = self.pkg_basename + "_" + pkgbaseversion
+        origtargz = os.path.join(self.work_dir,
+                                 pkgbasename + ".orig.tar.gz")
+        self.create_tarball(origtargz, self.work_dir,
+                            os.path.basename(pkgbasedir))
+
+        changemsg = ("Update to SVN rev. %d" % (self.revision,))
+        self.copy_debian_directory(pkgbasedir, pkgbaseversion,
+                                   changemsg)
+
+        self.create_source_package(pkgbasedir, origtargz)
+        self.move_source_package(pkgbasename)
+
+
+class RevisionPackager(treepkg.packager.RevisionPackager):
+
+    source_packager_cls = SourcePackager
+
+
+class PackageTrack(treepkg.packager.PackageTrack):
+
+    revision_packager_cls = RevisionPackager
+
+
+class TestPackager(unittest.TestCase, FileTestMixin):
+
+    initial_files = [
+        ("README", "and miles to go before I sleep"),
+        ]
+
+    debian_files = [
+        ("debian",
+         [("control", """\
+Source: testpkg
+Priority: optional
+Maintainer: TreePKG <treepkg@example.com>
+Standards-Version: 3.7.2
+
+Package: testpkg
+Architecture: all
+Description: Test package for treepkg testsGerman (de) internationalized (i18n) files for KDE
+ This package contains the German internationalized (i18n) files for
+ all KDE core applications.
+"""),
+          ("changelog", """\
+testpkg (0-0) unstable; urgency=low
+
+  * Initial version
+
+ -- TreePKG <treepkg@example.com>  Thu,  8 Mar 2007 18:34:39 +0100
+"""),
+          ("rules", "binary: echo binary")])]
+
+    def setUp(self):
+        self.svndir = self.create_temp_dir(self.id() + "-svn")
+        self.svnworkdir = self.create_temp_dir(self.id() + "-svnwork")
+        self.trackdir = self.create_temp_dir(self.id() + "-track")
+        self.create_files(self.trackdir, self.debian_files)
+
+    def test(self):
+        baseurl = create_svn_repository(self.svndir)
+        svn_url = baseurl + "/trunk"
+        subversion.checkout(svn_url, self.svnworkdir)
+        add_svn_files(self.svnworkdir, self.initial_files,
+                      "Initial version")
+        rootcmd = os.path.join(os.path.dirname(__file__), os.pardir, "test",
+                               "mocksudopbuilder.py")
+        track = PackageTrack(name="testpkg", base_dir=self.trackdir,
+                             svn_url=svn_url, pbuilderrc="",
+                             root_cmd=[sys.executable, rootcmd],
+                             deb_email="treepkg@example.com",
+                             deb_fullname="treepkg tester")
+        group = PackagerGroup([track], 1)
+        group.check_package_tracks()
+
+        # Now check the source and binary package files
+        pkgdir = os.path.join(self.trackdir, "pkg", "2-1")
+        self.assertEquals(sorted(os.listdir(os.path.join(pkgdir, "src"))),
+                          ["testpkg_2-kk1.diff.gz",
+                           "testpkg_2-kk1.dsc",
+                           "testpkg_2.orig.tar.gz"])
+        self.assertEquals(sorted(os.listdir(os.path.join(pkgdir, "binary"))),
+                          ["testpkg_2-kk1_all.deb",
+                           "testpkg_2-kk1_i386.changes"])
--- a/treepkg/packager.py	Fri Aug 31 17:07:18 2007 +0000
+++ b/treepkg/packager.py	Mon Sep 10 17:13:33 2007 +0000
@@ -195,6 +195,11 @@
                            logfile=self.logfile, bindir=self.binary_dir,
                            dsc=self.dsc_file),
                  suppress_output=True)
+        # remove the source package files put into the binary directory
+        # by pbuilder
+        for filename in os.listdir(self.binary_dir):
+            if os.path.splitext(filename)[1] not in (".deb", ".changes"):
+                os.remove(os.path.join(self.binary_dir, filename))
         self.status.binary_package_created()
 
 
This site is hosted by Intevation GmbH (Datenschutzerklärung und Impressum | Privacy Policy and Imprint)