# HG changeset patch # User Bernhard Herzog # Date 1189444413 0 # Node ID 98a7d70746a9431fe102901988eeb50dc22fc0b5 # Parent 83e48a76f759034eda99bb253ebe4e0a3284bee7 Make BinaryPackager remove all files that do not belong to the binary package from the pkg//binary directory after pbuilder finished. Also, add tests for this and some corresponding test support code. diff -r 83e48a76f759 -r 98a7d70746a9 test/filesupport.py --- /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 +# +# 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) diff -r 83e48a76f759 -r 98a7d70746a9 test/mocksudopbuilder.py --- /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 +# +# 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() diff -r 83e48a76f759 -r 98a7d70746a9 test/test_packager.py --- /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 +# +# 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 +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 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"]) diff -r 83e48a76f759 -r 98a7d70746a9 treepkg/packager.py --- 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()