Mercurial > treepkg
changeset 440:383d23c6bfde
Merged revisions 346-386 via svnmerge from
svn+ssh://svn.wald.intevation.org/treepkg/branches/treepkg-status
........
r346 | bricks | 2010-07-07 17:40:48 +0200 (Mi, 07 Jul 2010) | 2 lines
added classed for treepkg status xml generation
........
r347 | bricks | 2010-07-08 12:07:39 +0200 (Do, 08 Jul 2010) | 10 lines
renamed new status dir to info because of a naming conflict with status.py
let the user specify a treepkg name in the config
the name is propagated to PackagerGroup
[treepkg]
name: <treepkgname>
becomes:
pg = PackagerGroup(...)
pg.name
........
r348 | bricks | 2010-07-08 15:25:07 +0200 (Do, 08 Jul 2010) | 3 lines
moved test file to test main dir
fixed missing : after def statement
........
r349 | bricks | 2010-07-08 17:59:52 +0200 (Do, 08 Jul 2010) | 2 lines
added treepkg_dir andd tracks_dir attributes to PackageGroup
........
r350 | bricks | 2010-07-08 18:00:32 +0200 (Do, 08 Jul 2010) | 2 lines
changed <millpath> to <trackspath>
........
r351 | bricks | 2010-07-08 18:08:39 +0200 (Do, 08 Jul 2010) | 2 lines
check if config file exists to get usefull error output
........
r352 | bricks | 2010-07-08 18:11:13 +0200 (Do, 08 Jul 2010) | 2 lines
write first info about tracks
........
r353 | bricks | 2010-07-09 12:20:49 +0200 (Fr, 09 Jul 2010) | 2 lines
get all log files
........
r354 | bricks | 2010-07-09 14:42:42 +0200 (Fr, 09 Jul 2010) | 2 lines
added md5sum function
........
r355 | bricks | 2010-07-09 15:15:15 +0200 (Fr, 09 Jul 2010) | 4 lines
implemented nearly all info
only arch and os info are missing
........
r356 | bricks | 2010-07-09 16:19:17 +0200 (Fr, 09 Jul 2010) | 2 lines
Bugfix: source was not defined
........
r357 | bricks | 2010-07-09 16:44:52 +0200 (Fr, 09 Jul 2010) | 3 lines
fixed some typos
added missing add_checksum methond
........
r358 | bricks | 2010-07-09 17:06:15 +0200 (Fr, 09 Jul 2010) | 2 lines
fixed status line for revision
........
r359 | bricks | 2010-07-12 14:10:34 +0200 (Mo, 12 Jul 2010) | 2 lines
let the user specify num revisions that should be shown in the info
........
r360 | bricks | 2010-07-12 14:21:39 +0200 (Mo, 12 Jul 2010) | 2 lines
missed in last commit
........
r361 | bricks | 2010-07-12 17:06:22 +0200 (Mo, 12 Jul 2010) | 2 lines
add os config statement
........
r362 | bricks | 2010-07-12 17:47:42 +0200 (Mo, 12 Jul 2010) | 3 lines
moved packages and logs to revision tag
implemented os and arch info
........
r363 | bricks | 2010-07-13 14:23:28 +0200 (Di, 13 Jul 2010) | 2 lines
updated test readconfig for os config variable
........
r364 | bricks | 2010-07-13 14:24:14 +0200 (Di, 13 Jul 2010) | 2 lines
moved common publish functions to a seperate module
........
r365 | bricks | 2010-07-13 16:26:11 +0200 (Di, 13 Jul 2010) | 2 lines
moved arch info to each package info
........
r366 | bricks | 2010-07-13 18:46:17 +0200 (Di, 13 Jul 2010) | 2 lines
inital checkin for new publishpackages processing
........
r367 | bricks | 2010-07-21 17:52:15 +0200 (Mi, 21 Jul 2010) | 2 lines
implemented parsing from xml string
........
r368 | bricks | 2010-07-22 12:17:42 +0200 (Do, 22 Jul 2010) | 2 lines
copy binary-all packages in all binary-xyz dirs
........
r369 | bricks | 2010-07-22 12:26:40 +0200 (Do, 22 Jul 2010) | 2 lines
fixed small bugs
........
r370 | bricks | 2010-07-22 13:02:36 +0200 (Do, 22 Jul 2010) | 2 lines
added a new root element which is called TreepkgRoot for Treepkg Info classes
........
r371 | bricks | 2010-07-22 13:09:52 +0200 (Do, 22 Jul 2010) | 3 lines
fixed some small issues
remember: run pychecker BEFORE the commit
........
r372 | bricks | 2010-07-22 18:01:43 +0200 (Do, 22 Jul 2010) | 2 lines
improved publishdebianpackages and fixed a lot of bugs in the xml info parsing part
........
r373 | bricks | 2010-07-23 18:40:38 +0200 (Fr, 23 Jul 2010) | 2 lines
implemented CacheDb to store copied Packages from build host
........
r374 | bricks | 2010-07-26 10:28:48 +0200 (Mo, 26 Jul 2010) | 2 lines
moved data handling in publishdebianpackages into seperate module
........
r375 | bricks | 2010-07-26 11:42:07 +0200 (Mo, 26 Jul 2010) | 2 lines
fixed some typos
........
r376 | bricks | 2010-07-26 12:12:40 +0200 (Mo, 26 Jul 2010) | 2 lines
first version of incremental copying debian packages to cachedir
........
r377 | bricks | 2010-07-26 14:58:50 +0200 (Mo, 26 Jul 2010) | 2 lines
incremental copying only changed debian packages
........
r378 | bricks | 2010-07-26 15:39:10 +0200 (Mo, 26 Jul 2010) | 3 lines
use md5 instead of hashlib module
(md5 is deprecated in favour of hashlib in python 2.5)
........
r379 | bricks | 2010-07-26 15:46:19 +0200 (Mo, 26 Jul 2010) | 2 lines
make treepkg compatible to python 2.4
........
r380 | bricks | 2010-07-26 15:50:49 +0200 (Mo, 26 Jul 2010) | 2 lines
fixed last commit (db instead of sqlite3)
........
r381 | bricks | 2010-07-27 10:54:15 +0200 (Di, 27 Jul 2010) | 2 lines
fixed wrong import statement
........
r382 | bricks | 2010-07-27 10:54:34 +0200 (Di, 27 Jul 2010) | 2 lines
removed unnecessary debug output
........
r383 | bricks | 2010-07-27 18:28:22 +0200 (Di, 27 Jul 2010) | 2 lines
check if upload hook is empty
........
r384 | bricks | 2010-07-28 09:35:21 +0200 (Mi, 28 Jul 2010) | 2 lines
run rsync only an arch dirs that are present in the current treepkg
........
r385 | bricks | 2010-07-28 09:35:54 +0200 (Mi, 28 Jul 2010) | 2 lines
add helper tool to list content of a cache db
........
r386 | bricks | 2010-07-28 10:33:48 +0200 (Mi, 28 Jul 2010) | 3 lines
fixed a typo
regex fails if option is empty
........
author | Bjoern Ricks <bricks@intevation.de> |
---|---|
date | Fri, 06 Aug 2010 13:28:47 +0000 |
parents | 25d702bb13f9 (current diff) 56f7da71d41e (diff) |
children | eadcb1bb54e2 |
files | bin/publishpackages.py treepkg/packager.py |
diffstat | 18 files changed, 1302 insertions(+), 57 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bin/listcachedb.py Fri Aug 06 13:28:47 2010 +0000 @@ -0,0 +1,31 @@ +#! /usr/bin/python +# Copyright (C) 2010 by Intevation GmbH +# Authors: +# 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. + +from optparse import OptionParser + +import treepkgcmd +from treepkg.info.data import CacheDb + +def main(): + parser = OptionParser() + parser.add_option("--db", help=("path to the CacheDb")) + options, args = parser.parse_args() + + cachedb = CacheDb(options.db) + packages = cachedb.get_packages() + for package in packages: + print "=============================" + print "name: %s" % package.name + print "=============================" + print "binary: %s" % package.filename + print "source: %s" % package.sourcepath + print "arch: %s" % package.arch + print "md5sum: %s" % package.md5sum + +if __name__ == "__main__": + main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bin/publishdebianpackages.py Fri Aug 06 13:28:47 2010 +0000 @@ -0,0 +1,251 @@ +#! /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 os.path +import re +import sys +import shlex + +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 +from treepkg.info.data import Package +from treepkg.info.data import CacheDb + +EMPTY = re.compile(r'\s*') + +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), + ("cachedb", + lambda s: expand_filename(remove_trailing_slashes(s))), + ("cachedir", + lambda s: expand_filename(remove_trailing_slashes(s)))] + + +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" + " --only-successful", + 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_to_destdir(destdir, files, variables, quiet = False): + scp_flags = [] + if quiet: + scp_flags.append("-q") + + if not os.path.exists(destdir): + os.makedirs(destdir) + if files: + if variables["build_host"]: + userhost = "%(build_user)s@%(build_host)s:" % variables + files = [userhost + filename for filename in files] + # scp the packages to the cache dir + call(cmdexpand("scp -p @scp_flags @files $cachedir/", files=files, + scp_flags=scp_flags, cachedir=destdir)) + +def remove_old_packages(cachedb, newpackages, quiet): + newfiles = [package.filename for package in newpackages] + oldpackages = cachedb.get_old_packages(newfiles) + for package in oldpackages: + # better check if the file really exists + if os.path.isfile(package.filename): + if not quiet: + print "removing file %s" % package.filename + os.remove(package.filename) + cachedb.remove_packages(oldpackages) + +def copy_packages_to_destdir(cachedb, dir, packages, variables, quiet = False): + packages.sort(cmp=sort_trackname_arch) + package = packages[0] + trackname = package.trackname + arch = package.arch + destdir = os.path.join(dir, arch, trackname) + files = [] + for package in packages: + cachedb.add_package(package) + if package.trackname != trackname or \ + package.arch != arch: + copy_files_to_destdir(destdir, files, variables, quiet) + trackname = package.trackname + arch = package.arch + destdir = os.path.join(dir, arch, trackname) + files = [] + # add only to copy files list if the packages differ + if check_package_is_new(package.name, destdir, package.md5sum): + files.append(package.sourcepath) + if not quiet: + print "copy new file: %s" % package.name + copy_files_to_destdir(destdir, files, variables, quiet) + +def copy_to_cachedir(variables, track, revision, quiet = False, architectures=None): + cachedir = variables["cachedir"] + cachdebfilename = variables["cachedb"] + if not quiet: + print "using cachedb: %s" % cachdebfilename + cachedb = CacheDb(cachdebfilename) + 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([]) + for track in treepkginfo.tracks: + for rev in track.revisions: + for packageinfo in rev.packages: + arch = get_binary_arch(packageinfo.arch) + if packageinfo.type == "binary": + # skip other files + if packageinfo.arch is None: + continue + # handle binary-all + if arch == "binary-all": + # add trackname for subdir name + packageinfo.trackname = track.name + binaryallpackages.append(packageinfo) + continue + allarchs.add(arch) + elif packageinfo.type == "source": + arch = packageinfo.type + # only copy requested archs + if len(allowedarchs) == 0 or \ + arch in allowedarchs: + filename = os.path.join(cachedir, arch, track.name, + packageinfo.name) + newpackage = Package(filename, track.name, packageinfo.name, + packageinfo.path, arch, + get_md5sum(packageinfo)) + 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 packageinfo in binaryallpackages: + for arch in binallarchs: + filename = os.path.join(cachedir, arch, packageinfo.trackname, + packageinfo.name) + newpackage = Package(filename, packageinfo.trackname, packageinfo.name, + packageinfo.path, arch, get_md5sum(packageinfo)) + newpackages.append(newpackage) + copy_packages_to_destdir(cachedb, cachedir, newpackages, variables, quiet) + remove_old_packages(cachedb, newpackages, quiet) + return binallarchs + +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"] + allarchs = copy_to_cachedir(config, track, revision, quiet, architectures) + for arch in allarchs: + copy_to_publishdir(config, dist, section, arch, quiet) + + # update apt archive + if not config["after_upload_hook"] or \ + not EMPTY.match(config["after_upload_hook"]): + if not quiet: + print "running after upload hook" + 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()
--- a/bin/publishpackages.py Wed Jul 28 08:35:07 2010 +0000 +++ b/bin/publishpackages.py Fri Aug 06 13:28:47 2010 +0000 @@ -20,16 +20,8 @@ from treepkg.run import call, capture_output from treepkg.cmdexpand import cmdexpand from treepkg.util import ensure_directory, listdir_abs - - -def remove_trailing_slashes(s): - return s.rstrip("/") - -def expand_filename(filename): - """ - Applies os.path.expanduser and os.path.expandvars to filename - """ - return os.path.expandvars(os.path.expanduser(filename)) +from treepkg.publish import remove_trailing_slashes, expand_filename +from treepkg.publish import prefix_for_remote_command, copy_to_publishdir config_desc = ["distribution", "section", "num_newest", "build_user", "build_host", "build_listpackages", @@ -74,18 +66,6 @@ " informational output")) return parser.parse_args() -def prefix_for_remote_command(user, host): - """Returns the ssh call needed to run a command on a remote host. - If host is empty, the function assumes the command is to be run on - the local host as the same user that exectutes this function, so - that no ssh or other call is needed. - """ - prefix = [] - if host: - prefix.extend(["ssh", "%s@%s" % (user, host)]) - return prefix - - def copy_to_cache(variables, track, revision, arch, quiet): listpackages_vars = variables.copy() @@ -126,29 +106,6 @@ call(cmdexpand("scp -p @scp_flags @files $cachedir/", files=files, scp_flags=scp_flags, **variables)) - -def copy_to_publishdir(variables, dist, section, arch, quiet): - destdir = os.path.join(variables["publish_dir"], dist, section, arch) - remote_destdir = destdir - if variables["publish_host"]: - remote_destdir = (("%(publish_user)s@%(publish_host)s:" % variables) - + remote_destdir) - runremote = prefix_for_remote_command(variables["publish_user"], - variables["publish_host"]) - - call(cmdexpand("@runremote mkdir --parents $destdir", - runremote=runremote, destdir=destdir, **variables)) - rsync_flags = [] - if variables["publish_remove_old_packages"]: - rsync_flags.append("--delete") - if quiet: - rsync_flags.append("--quiet") - call(cmdexpand("rsync @rsync_flags -r --perms --times --omit-dir-times" - " $cachedir/ $remote_destdir/", - rsync_flags=rsync_flags, remote_destdir=remote_destdir, - **variables)) - - def publish_packages_arch(variables, track, revision, dist, section, arch, quiet): copy_to_cache(variables, track, revision, arch, quiet)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bin/treepkginfo.py Fri Aug 06 13:28:47 2010 +0000 @@ -0,0 +1,41 @@ +#! /usr/bin/python +# Copyright (C) 2010 by Intevation GmbH +# Authors: +# 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. + +"""Receive the status of a treepkg""" + +import treepkgcmd + +from treepkg.info import status + +from treepkg.options import create_parser + +def parse_commandline(): + parser = create_parser() + parser.set_defaults(newest=-1) + parser.add_option("--newest", type="int", + help=("Number of newest revisions to list.")) + parser.add_option("--only-successful", action="store_true", + help=("Show only successful builds"), dest="success") + parser.add_option("--pretty", action="store_true", help=("Show XML pretty" + " output.")) + + + return parser.parse_args() + +def main(): + options, args = parse_commandline() + + tpkginfo = status.TreepkgInfo(options.config_file, options.newest, + options.success) + + if options.pretty: + print tpkginfo.toxml().toprettyxml() + else: + print tpkginfo.toxml().toxml() + +main()
--- a/demopublishpackages.cfg Wed Jul 28 08:35:07 2010 +0000 +++ b/demopublishpackages.cfg Fri Aug 06 13:28:47 2010 +0000 @@ -59,3 +59,4 @@ # ~user at the beginning of the value and environment variable # references of the form $VAR or ${VAR} are expanded. cachedir: /tmp/${LOGNAME}/treepkg-package-cache/%(build_host)s +cachedb: /tmp/${LOGNAME}/treepkg-package-cache/%(build_host)s-cache.db
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/test_info.py Fri Aug 06 13:28:47 2010 +0000 @@ -0,0 +1,99 @@ +# Copyright (C) 2010 by Intevation GmbH +# Authors: +# 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. + +"""Tests for treepkg.status.status""" + +import unittest +import os.path +import sys + +test_dir = os.path.dirname(__file__) +sys.path.append(os.path.join(test_dir, os.pardir)) + +from treepkg.info.status import * +from treepkg.report import get_packager_group +from filesupport import FileTestMixin + +from publishdebianpackages import get_binary_arch + +class TreepkgInfoTest(unittest.TestCase, FileTestMixin): + config_contents = """\ +[DEFAULT] +#treepkg_dir: /home/builder/mill +#tracks_dir: %(treepkg_dir)s/tracks +tracks_dir: /home/builder/mill/tracks +root_cmd: sudo +pbuilder_dir: %(tracks_dir)s/pbuilder +builderconfig: %(pbuilder_dir)s/pbuilderrc +deb_email: treepkg@example.com +deb_fullname: TreePKG +pkg_revision_template: treepkg%%(pkg_revision)s +handle_dependencies: False + +[treepkg] +instructions_file: %(tracks_dir)s/instructions +check_interval: 3600 +name: testtreepkginfo + +[pkg_simple] +pkg_basename: simple1 +svn_url: svn://example.com/%(name)s/trunk +base_dir: %(tracks_dir)s/%(name)s +packager_class: treepkginfo_test.%(name)s +handle_dependencies: True +signing_key_id: abcd1234 +changelog_msg_template: Update to feature branch r%%(revision)s +version_template: 1.0.svn%%(revision)s + +""" + + files = [("treepkg.cfg", config_contents), + ("treepkginfo_test", + [("__init__.py", ""), + ("simple.py", + "\n".join(["class SourcePackager:", + " pass", + ""])), + ])] + + def setUp(self): + self.directory = self.create_files("treepkg", self.files) + self.old_path = sys.path + sys.path = [self.directory] + sys.path + + def tearDown(self): + sys.path = self.old_path + + def test_createinfo(self): + config_file = os.path.join(self.directory, "treepkg.cfg") + tpkginfo = TreepkgInfo(config_file) + tpkgrootinfo = tpkginfo.tpkgroot.info + self.assertEquals("testtreepkginfo", tpkgrootinfo.name) + + +class TreepkgRootInfoTest(unittest.TestCase): + + def test_toxml(self): + status = TreepkgRootInfo("testtreepkg") + dom = status.toxml() + xml = dom.toxml() + self.assertEquals("<info><name>testtreepkg</name></info>", xml) + +class TestPublishDebianPackages(unittest.TestCase, FileTestMixin): + + def test_get_binary_arch(self): + source = get_binary_arch("source") + self.assertEquals("source", source) + binary_armel = get_binary_arch("armel") + self.assertEquals("binary-armel", binary_armel) + binary_armel = get_binary_arch("binary-armel") + self.assertEquals("binary-armel", binary_armel) + + +if __name__ == '__main__': + unittest.main() +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/test_info_data.py Fri Aug 06 13:28:47 2010 +0000 @@ -0,0 +1,74 @@ +# Copyright (C) 2010 by Intevation GmbH +# Authors: +# 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. + +"""Tests for treepkg.info.data""" + +import unittest +import os.path +import sys + +test_dir = os.path.dirname(__file__) +sys.path.append(os.path.join(test_dir, os.pardir)) + +from treepkg.info.data import Package +from treepkg.info.data import CacheDb +from filesupport import FileTestMixin + +class TestCacheDb(unittest.TestCase, FileTestMixin): + + def test_cache_db(self): + tmpdir = self.create_test_specific_temp_dir() + package = Package("/tmp/abc/abc_0.1_i386.deb", "abc", + "abc_0.1_i386.deb", "/source/abc/abc_0.1_i386.deb", + "binary-i386", "1234567") +# tmpfile = self.create_temp_file("cachedb1", "abc") +# with self.assertRaises(Exception): +# db = CacheDb(tmpfile) + + dbfile = os.path.join(tmpdir, "cachedb2") + db = CacheDb(dbfile) + # insert + filename = package.filename + db.add_package(package) + package = db.get_package(filename) + self.assertEquals("1234567", package.md5sum) + # update + package.md5sum = "01234567" + db.add_package(package) + package = db.get_package(filename) + self.assertEquals("01234567", package.md5sum) + + # test get_package + package2 = db.get_package(package.filename) + self.assertNotEquals(None, package2) + self.assertEquals(package.filename, package2.filename) + self.assertEquals(package.name, package2.name) + self.assertEquals(package.sourcepath, package2.sourcepath) + self.assertEquals(package.arch, package2.arch) + self.assertEquals(package.md5sum, package2.md5sum) + + package3 = Package("/tmp/foo/foo_0.2.i386.deb", "foo", + "foo_0.2_i386.deb", "/tmp/source/foo/foo_0.2.i386.deb", + "binary-i386", "987654321") + package4 = Package("/tmp/john/doe_0.3.i386.deb", "john", + "doe_0.3.i386.deb", "/tmp/source/john/doe_0.3.i386.deb", + "binary-i386", "5671234") + db.add_package(package3) + db.add_package(package4) + + # test get_old_packages + oldpackages = db.get_old_packages([package.filename]) + self.assertEquals(2, len(oldpackages)) + packages = db.get_packages() + self.assertEquals(3, len(packages)) + db.remove_packages(oldpackages) + packages = db.get_packages() + self.assertEquals(1, len(packages)) + +if __name__ == '__main__': + unittest.main() +
--- a/test/test_readconfig.py Wed Jul 28 08:35:07 2010 +0000 +++ b/test/test_readconfig.py Fri Aug 06 13:28:47 2010 +0000 @@ -34,6 +34,7 @@ [treepkg] instructions_file: %(treepkg_dir)s/instructions check_interval: 3600 +name: testtreepkg [pkg_simple] pkg_basename: simple1 @@ -44,6 +45,7 @@ signing_key_id: abcd1234 changelog_msg_template: Update to feature branch r%%(revision)s version_template: 1.0.svn%%(revision)s +os: foo os [pkg_extraargs] svn_url: svn://example.com/%(name)s/trunk @@ -55,6 +57,7 @@ orig_tarball: %(base_dir)s/mytarball.tgz rules_svn_url: file:///tmp/my-debian-repository deb_build_options: parallel=2 +os: bar os """ files = [("treepkg.cfg", config_contents), @@ -82,7 +85,10 @@ treepkg_opts, packager_opts = read_config(config_file) self.assertEquals(treepkg_opts, dict(instructions_file="/home/builder/mill/instructions", - check_interval=3600)) + check_interval=3600, + name="testtreepkg", + treepkg_dir="/home/builder/mill", + tracks_dir="/home/builder/mill/tracks")) self.assertEquals(sorted(packager_opts, key=operator.itemgetter("name")), [ @@ -111,7 +117,8 @@ "tracks/extraargs/mytarball.tgz"), git_url="", git_branch="", - builder_cls="PBuilder"), + builder_cls="PBuilder", + os="bar os"), dict(name="simple", base_dir="/home/builder/mill/tracks/simple", changelog_msg_template=("Update to feature branch" @@ -133,4 +140,11 @@ version_template="1.0.svn%(revision)s", git_url="", git_branch="", - builder_cls="PBuilder")]) + builder_cls="PBuilder", + os="foo os")]) + +def main(): + unittest.main() + +if __name__ == "__main__": + main()
--- a/test/test_util.py Wed Jul 28 08:35:07 2010 +0000 +++ b/test/test_util.py Fri Aug 06 13:28:47 2010 +0000 @@ -12,7 +12,7 @@ from filesupport import FileTestMixin -from treepkg.util import replace_in_file, listdir_abs +from treepkg.util import replace_in_file, listdir_abs, md5sum class TestReplaceInFile(unittest.TestCase, FileTestMixin): @@ -72,3 +72,13 @@ self.assertEquals(sorted(listdir_abs(directory, '*.dsc')), [os.path.join(directory, "foo.dsc")]) +class TestMd5sum(unittest.TestCase, FileTestMixin): + + content = "this is a test content" + + def setUp(self): + self.testfile = self.create_temp_file("testmd5.txt", self.content) + + def test_md5sum(self): + sum = md5sum(self.testfile) + self.assertEquals("a12511153555c1f0f0a1eda200733a3f", sum)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/treepkg/info/__init__.py Fri Aug 06 13:28:47 2010 +0000 @@ -0,0 +1,6 @@ +# 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.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/treepkg/info/data.py Fri Aug 06 13:28:47 2010 +0000 @@ -0,0 +1,101 @@ +#! /usr/bin/python +# Copyright (C) 2010 by Intevation GmbH +# Authors: +# 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. +""" Classes for storing and handling data related to treepkg info """ + +try: + import sqlite3 as db +except ImportError: + from pysqlite2 import dbapi2 as db + +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 = db.connect(file) + self.cursor = self.conn.cursor() + self.init_db() + + def init_db(self): + 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, packages): + DELET_TMPL = """DELETE FROM packages + WHERE filename in (%s)""" + tmp = ", ".join(['?'] * len(packages)) + self.cursor.execute(DELET_TMPL % tmp, + [package.filename for package in packages]) + 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()] +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/treepkg/info/package.py Fri Aug 06 13:28:47 2010 +0000 @@ -0,0 +1,44 @@ +# Copyright (C) 2010 by Intevation GmbH +# Authors: +# 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. + +"""Classes to read information from binary packages """ + +import os.path + +from treepkg import run + +from treepkg.cmdexpand import cmdexpand + + +class DebianPackage: + + def __init__(self, filename): + self.filename = filename + + def is_package(self): + extension = os.path.splitext(self.filename)[1] + return extension.lower() == ".deb" + + def get_architecture(self): + if not self.is_package(): + return None + arch = run.capture_output(cmdexpand("dpkg-deb -f $debfile Architecture", + debfile=self.filename)) + return arch.strip() + +class BinaryPackage: + + def __init__(self, filename): + self.filename = filename + self.packagetypes = [] + self.packagetypes.append(DebianPackage(self.filename)) + + def get_architecture(self): + for packagetype in self.packagetypes: + retval = packagetype.get_architecture() + if not retval is None: + return retval
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/treepkg/info/status.py Fri Aug 06 13:28:47 2010 +0000 @@ -0,0 +1,423 @@ +# Copyright (C) 2010 by Intevation GmbH +# Authors: +# 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. + +"""Build treepkg status information""" + +import os.path +import xml.dom.minidom + +from treepkg.report import get_packager_group +from treepkg.util import md5sum +from treepkg.info.package import BinaryPackage + +TREEPKG_NAMESPACE_URI = "http://wald.intevation.org/projects/treepkg/" +TREEPKG_NAMESPACE_PREFIX = "tpkg" + +def createTpkgElement(doc, name): + return doc.createElementNS(TREEPKG_NAMESPACE_URI, name) + +def createTpkgRoot(name): + domimpl = xml.dom.minidom.getDOMImplementation() + doc = domimpl.createDocument(TREEPKG_NAMESPACE_URI, name, None) + root = doc.documentElement + return (doc, root) + +def getTextFromNode(node): + if not node: + return None + textnode = node.firstChild + if not textnode or textnode.nodeType != node.TEXT_NODE: + return None + return textnode.data + +def getChild(node, name, required=False): + childs = getChilds(node, name, required) + if not childs or len(childs) == 0: + return None + else: + return childs[0] + +def getChilds(node, name, required=False): + if not node: + if required: + raise TreepkgInfoException("Element %s is required as child. But" + "parent element is not available.") + return None + childs = node.getElementsByTagName(name) + if not childs: + if required: + raise TreepkgInfoException("Element %s is required as child for %s." + "The XML file must be invalid." % (name, node.nodeName)) + return None + return childs + + +class TreepkgInfoException(Exception): + """ Exception class for TreepkgInfo """ + +class TreepkgInfo: + + def __init__(self, config, numnewestrev=-1, success=False): + self.numnewestrev = numnewestrev + self.success = success + group = get_packager_group(config) + treepkgrootinfo = TreepkgRootInfo(group.name, group.treepkg_dir, + group.tracks_dir) + version = "1.0" + self.tpkgroot = TreepkgRoot(version, treepkgrootinfo) + tracks = group.get_package_tracks() + + for track in tracks: + trackinfo = TreepkgTrackInfo(track.name) + self.tpkgroot.add_track(trackinfo) + self.add_revisions(track, trackinfo) + + def toxml(self): + return self.tpkgroot.toxml() + + @staticmethod + def fromxml(xmlstr): + doc = xml.dom.minidom.parseString(xmlstr) + root = doc.documentElement + if not root.tagName == "treepkg": + raise TreepkgInfoException("XML is not valid for treepkginfo") + return TreepkgRoot.fromxml(root) + + def add_revisions(self, track, trackinfo): + revisions = track.get_revisions() + revisions = sorted(revisions, key=lambda r: r.status.start, + reverse=True) + if self.numnewestrev > 0: + revisions = revisions[:self.numnewestrev] + + arch = None + + for rev in revisions: + if self.success: # skip not successful builds + if not rev.status.status.finished \ + or rev.status.status.error: + continue + revision = rev.revision + rules_revision = rev.rules_revision + status = rev.status.status.description # extend status + + sources = rev.list_source_files() + binaries = rev.list_binary_files() + + revinfo = TreepkgTrackRevisionInfo(revision, rules_revision, + status) + logs = rev.get_log_files() + for (title, filename) in logs: + loginfo = TreepkgLogInfo(title, filename) + revinfo.add_log(loginfo) + for source in sources: + self.add_package(revinfo, source, "source") + for binary in binaries: + binpackage = BinaryPackage(binary) + arch = binpackage.get_architecture() + self.add_package(revinfo, binary, "binary", arch) + + trackinfo.add_revision(revinfo) + + def add_package(self, revision, file, type, arch=None): + name = os.path.basename(file) + checksum = md5sum(file) + checksuminfo = TreepkgChecksumInfo(checksum, "md5") + pkginfo = TreepkgPackageInfo(name, file, type, arch) + pkginfo.add_checksum(checksuminfo) + revision.add_package(pkginfo) + +class TreepkgRoot: + + def __init__(self, version, info): + self.version = version + self.info = info + self.tracks = [] + + def add_track(self, track): + self.tracks.append(track) + + def toxml(self): + (doc, root) = createTpkgRoot("treepkg") + root.setAttributeNS(TREEPKG_NAMESPACE_URI, "version", self.version) + if len(self.tracks) > 0: + tracksele = createTpkgElement(doc, "tracks") + for track in self.tracks: + tracksele.appendChild(track.toxml()) + root.appendChild(tracksele) + root.appendChild(self.info.toxml()) + return root + + @staticmethod + def fromxml(node): + version = node.getAttribute("version") + tracksele = getChild(node, "tracks") + trackeles = getChilds(tracksele, "track") + infoele = getChild(node, "info") + treepkgrootinfo = TreepkgRootInfo.fromxml(infoele) + treepkgroot = TreepkgRoot(version, treepkgrootinfo) + tracks = [] + for trackele in trackeles: + tracks.append(TreepkgTrackInfo.fromxml(trackele)) + treepkgroot.tracks = tracks + return treepkgroot + +class TreepkgRootInfo: + + def __init__(self, name, treepkgpath=None, trackspath=None, version=None): + self.name = name + self.treepkgpath = treepkgpath + self.trackspath = trackspath + self.version = version + + def toxml(self): + (doc, root) = createTpkgRoot("info") + # add <name> + nameele = createTpkgElement(doc, "name") + text = doc.createTextNode(self.name) + nameele.appendChild(text) + root.appendChild(nameele) + # add <treepkgpath> + if self.treepkgpath: + treepkgpathele = createTpkgElement(doc, "treepkgpath") + text = doc.createTextNode(self.treepkgpath) + treepkgpathele.appendChild(text) + root.appendChild(treepkgpathele) + # add <trackspath> + if self.trackspath: + trackspathele = createTpkgElement(doc, "trackspath") + text = doc.createTextNode(self.trackspath) + trackspathele.appendChild(text) + root.appendChild(trackspathele) + # add <version> + if self.version: + versionele = createTpkgElement(doc, "version") + text = doc.createTextNode(self.version) + versionele.appendChild(text) + root.appendChild(versionele) + return root + + @staticmethod + def fromxml(node): + version = node.getAttribute("version") + nameele = getChild(node, "name", True) + name = getTextFromNode(nameele) + treepkgpathele = getChild(node, "treepkgpath") + treepkgpath = getTextFromNode(treepkgpathele) + trackspathele = getChild(node, "trackspath") + trackspath = getTextFromNode(trackspathele) + return TreepkgRootInfo(name, treepkgpath, trackspath, version) + +class TreepkgTrackInfo: + + def __init__(self, name, os=None): + self.name = name + self.os = os + self.revisions = [] + + def add_revision(self, revision): + self.revisions.append(revision) + + def toxml(self): + (doc, root) = createTpkgRoot("track") + nameele = createTpkgElement(doc, "name") + text = doc.createTextNode(self.name) + nameele.appendChild(text) + root.appendChild(nameele) + for rev in self.revisions: + root.appendChild(rev.toxml()) + if not self.os is None: + osele = createTpkgElement(doc, "os") + text = doc.createTextNode(self.os) + osele.appendChild(text) + root.appendChild(osele) + return root + + @staticmethod + def fromxml(node): + nameele = getChild(node, "name", True) + name = getTextFromNode(nameele) + osele = getChild(node, "os") + os = getTextFromNode(osele) + treepkgtrackinfo = TreepkgTrackInfo(name, os) + treepkgtrackinfo.revisions = [] + revisioneles = node.getElementsByTagName("revision") + for revele in revisioneles: + treepkgrevision = TreepkgTrackRevisionInfo.fromxml(revele) + treepkgtrackinfo.revisions.append(treepkgrevision) + return treepkgtrackinfo + + +class TreepkgTrackRevisionInfo: + + def __init__(self, number, rules, status): + self.number = number + self.rules = rules + self.status = status + self.packages = [] + self.logs = [] + + def add_package(self, package): + self.packages.append(package) + + def add_log(self, log): + self.logs.append(log) + + def toxml(self): + (doc, root) = createTpkgRoot("revision") + # add <number> + numberele = createTpkgElement(doc, "number") + text = doc.createTextNode(self.number) + numberele.appendChild(text) + root.appendChild(numberele) + # add <rules> + rulesele = createTpkgElement(doc, "rules") + text = doc.createTextNode(self.rules) + rulesele.appendChild(text) + root.appendChild(rulesele) + # add <status><message></message></status> + statusele = createTpkgElement(doc, "status") + messageele = createTpkgElement(doc, "message") + text = doc.createTextNode(self.status) + messageele.appendChild(text) + statusele.appendChild(messageele) + root.appendChild(statusele) + # add <packages> + packagesele = createTpkgElement(doc, "packages") + for package in self.packages: + packagesele.appendChild(package.toxml()) + root.appendChild(packagesele) + # add <logs> + logsele = createTpkgElement(doc, "logs") + for log in self.logs: + logsele.appendChild(log.toxml()) + root.appendChild(logsele) + return root + + @staticmethod + def fromxml(node): + numberele = getChild(node, "number", True) + number = getTextFromNode(numberele) + rulesele = getChild(node, "rules", True) + rules = getTextFromNode(rulesele) + statusele = getChild(node, "status", True) + messageele = getChild(statusele, "message") + message = getTextFromNode(messageele) + treepkgrevisioninfo = TreepkgTrackRevisionInfo(number, rules, message) + treepkgrevisioninfo.packages = [] + treepkgrevisioninfo.logs = [] + packagesele = getChild(node, "packages") + packageeles = getChilds(packagesele, "package") + for packageele in packageeles: + treepkgrevisioninfo.packages.append( + TreepkgPackageInfo.fromxml(packageele)) + logsele = getChild(node, "logs") + logeles = getChilds(logsele, "log") + for logele in logeles: + treepkgrevisioninfo.logs.append( + TreepkgLogInfo.fromxml(logele)) + return treepkgrevisioninfo + +class TreepkgLogInfo: + + def __init__(self, name, path): + self.name = name + self.path = path + + def toxml(self): + (doc, root) = createTpkgRoot("log") + # add <name> + nameele = createTpkgElement(doc, "name") + text = doc.createTextNode(self.name) + nameele.appendChild(text) + root.appendChild(nameele) + # add path + pathele = createTpkgElement(doc, "path") + text = doc.createTextNode(self.path) + pathele.appendChild(text) + root.appendChild(pathele) + return root + + @staticmethod + def fromxml(node): + nameele = getChild(node, "name", True) + name = getTextFromNode(nameele) + pathele = getChild(node, "path", True) + path = getTextFromNode(pathele) + return TreepkgLogInfo(name, path) + +class TreepkgPackageInfo: + + def __init__(self, name, path, type, arch): + self.name = name + self.path = path + self.type = type + self.arch = arch + self.checksums = [] + + def toxml(self): + (doc, root) = createTpkgRoot("package") + # add <name> + nameele = createTpkgElement(doc, "name") + text = doc.createTextNode(self.name) + nameele.appendChild(text) + root.appendChild(nameele) + # add <path> + pathele = createTpkgElement(doc, "path") + text = doc.createTextNode(self.path) + pathele.appendChild(text) + root.appendChild(pathele) + # add <checksum> + for checksum in self.checksums: + root.appendChild(checksum.toxml()) + # add <type> + root.setAttributeNS(TREEPKG_NAMESPACE_URI, "type", self.type) + if not self.arch is None: + root.setAttributeNS(TREEPKG_NAMESPACE_URI, "arch", self.arch) + return root + + def add_checksum(self, checksum): + self.checksums.append(checksum) + + @staticmethod + def fromxml(node): + nameele = getChild(node, "name", True) + name = getTextFromNode(nameele) + pathele = getChild(node, "path", True) + path = getTextFromNode(pathele) + ptype = node.getAttribute("type") + arch = node.getAttribute("arch") + if len(arch) == 0: + arch = None + packageinfo = TreepkgPackageInfo(name, path, ptype, arch) + checksumeles = node.getElementsByTagName("checksum") + for checksumele in checksumeles: + packageinfo.checksums.append(TreepkgChecksumInfo.fromxml(checksumele)) + return packageinfo + +class TreepkgChecksumInfo: + + def __init__(self, checksum, type="md5"): + self.checksum = checksum + self.type = type + + def toxml(self): + (doc, root) = createTpkgRoot("checksum") + text = doc.createTextNode(self.checksum) + root.appendChild(text) + # add attribute type + if type: + root.setAttributeNS(TREEPKG_NAMESPACE_URI, "type", self.type) + return root + + @staticmethod + def fromxml(node): + checksum = getTextFromNode(node) + ctype = node.getAttribute("type") + if len(ctype) == 0: + return TreepkgChecksumInfo(checksum) + return TreepkgChecksumInfo(checksum, ctype)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/treepkg/info/status.xsd Fri Aug 06 13:28:47 2010 +0000 @@ -0,0 +1,103 @@ +<?xml version="1.0" encoding="UTF-8"?> +<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" + xmlns:tpkg="http://wald.intevation.org/projects/treepkg/" + targetNamespace="http://wald.intevation.org/projects/treepkg/"> + + <xsd:element name="treepkg"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="tracks" type="tpkg:tracks"/> + <xsd:element name="info" type="tpkg:info" minOccurs="1"/> + </xsd:sequence> + <!-- xml schema version --> + <xsd:attribute name="version" type="xsd:string"/> + </xsd:complexType> + </xsd:element> + + <xsd:complexType name="info"> + <xsd:sequence> + <xsd:element name="name" type="xsd:string" minOccurs="1"/> + <xsd:element name="treepkgpath" type="xsd:string"/> + <xsd:element name="trackspath" type="xsd:string"/> + <!-- treepkg version e.g. svn revision --> + <xsd:element name="version" type="xsd:string"/> + </xsd:sequence> + </xsd:complexType> + + <xsd:complexType name="tracks"> + <xsd:sequence> + <xsd:element name="track" type="tpkg:track" maxOccurs="unbounded"/> + </xsd:sequence> + </xsd:complexType> + + <xsd:complexType name="track"> + <xsd:sequence> + <xsd:element name="name" type="xsd:string" minOccurs="1"/> + <xsd:element name="revision" type="tpkg:revision" + maxOccurs="unbounded"/> + <xsd:element name="os" type="xsd:string"/> + </xsd:sequence> + </xsd:complexType> + + <xsd:complexType name="revision"> + <xsd:sequence> + <xsd:element name="number" type="xsd:string" minOccurs="1"/> + <xsd:element name="rules" type="xsd:string" minOccurs="1"/> + <xsd:element name="status" type="tpkg:revisionstatus" minOccurs="1"/> + <xsd:element name="packages" type="tpkg:packages"/> + <xsd:element name="logs" type="tpkg:logs"/> + </xsd:sequence> + </xsd:complexType> + + <xsd:complexType name="revisionstatus"> + <xsd:sequence> + <xsd:element name="message" type="xsd:string"/> + </xsd:sequence> + </xsd:complexType> + + <xsd:complexType name="logs"> + <xsd:sequence> + <xsd:element name="log" type="tpkg:log" maxOccurs="unbounded"/> + </xsd:sequence> + </xsd:complexType> + + <xsd:complexType nam="log"> + <xsd:sequence> + <xsd:element name="name" type="xsd:string" minOccurs="1"/> + <xsd:element name="path" type="xsd:string" minOccurs="1"/> + </xsd:sequence> + </xsd:complexType> + + + <xsd:complexType name="packages"> + <xsd:sequence> + <xsd:element name="package" type="tpkg:package" + maxOccurs="unbounded"/> + </xsd:sequence> + </xsd:complexType> + + <xsd:complexType name="package"> + <xsd:sequence> + <xsd:element name="name" type="xsd:string" minOccurs="1"/> + <xsd:element name="path" type="xsd:string" minOccurs="1"> + <xsd:element name="checksum" type="tpkg:checksum" + maxOccurs="unbounded" default="md5"/> + </xsd:sequence> + <xsd:attribute name="type" type="tpkg:pkgtype" minOccurs="1"/> + <xsd:attribute name="arch" type="xsd:string"/> + </xsd:complexType> + + <xsd:complexType name="checksum"> + <xsd:sequence> + <xsd:element name="checksum" type="xsd:string" minOccurs="1"/> + </xsd:sequence> + <xsd:attribute name="type" type="xsd:string"/> + </xsd:complexType> + + <xsd:simpleType name="pkgtype"> + <xsd:restriction base="xsd:string"> + <xsd:enumeration value="binary"/> + <xsd:enumeration value="source"/> + </xsd:restriction> + </xsd:simpleType> +</xsd:schema>
--- a/treepkg/packager.py Wed Jul 28 08:35:07 2010 +0000 +++ b/treepkg/packager.py Fri Aug 06 13:28:47 2010 +0000 @@ -323,7 +323,7 @@ return self.build_log + ".gz" return self.build_log - def get_log_files(self, logs): + def get_log_files(self, logs=None): files = [] if os.path.isdir(self.log_dir): for f in os.listdir(self.log_dir): @@ -331,7 +331,7 @@ f = os.path.join(self.log_dir,f) if os.path.isfile(f): files.append((self.get_log_title(f),f)) - return files + return files def list_log_files(self, logs): """Returns a list describing the logfiles available for the revision. @@ -404,7 +404,8 @@ handle_dependencies=False, signing_key_id="", do_build=True, rules_svn_url=None, deb_build_options="", pkg_basename="", changelog_msg_template="Update to r%(revision)s", - svn_subset=(), svn_externals=(), git_branch="", git_url=""): + svn_subset=(), svn_externals=(), git_branch="", git_url="", + os=""): self.name = name # Convert the builder_cls option to a class @@ -435,6 +436,7 @@ self.do_build = do_build self.handle_dependencies = handle_dependencies self.dependencies = None + self.os = os self.pkg_dir_template = "%(revision)s-%(rules_revision)s" self.pkg_dir_regex = re.compile(r"(?P<revision>[0-9a-f]+)" r"-(?P<rules_revision>[0-9a-f]+)$") @@ -642,7 +644,8 @@ def __init__(self, package_tracks, check_interval, revision=None, instructions_file=None, do_svn_update=True, - stop_on_error=False): + stop_on_error=False, name="", treepkg_dir=None, + tracks_dir=None): self.package_tracks = package_tracks self.check_interval = check_interval self.revision = revision @@ -650,6 +653,9 @@ self.stop_on_error = stop_on_error self.instructions_file = instructions_file self.instructions_file_removed = False + self.name = name + self.treepkg_dir = treepkg_dir + self.tracks_dir = tracks_dir self.sort_tracks() def sort_tracks(self):
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/treepkg/publish.py Fri Aug 06 13:28:47 2010 +0000 @@ -0,0 +1,59 @@ +#! /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. + +import os.path + +from treepkg.run import call, capture_output +from treepkg.cmdexpand import cmdexpand + +def remove_trailing_slashes(s): + return s.rstrip("/") + +def expand_filename(filename): + """ + Applies os.path.expanduser and os.path.expandvars to filename + """ + return os.path.expandvars(os.path.expanduser(filename)) + +def prefix_for_remote_command(user, host): + """Returns the ssh call needed to run a command on a remote host. + If host is empty, the function assumes the command is to be run on + the local host as the same user that exectutes this function, so + that no ssh or other call is needed. + """ + prefix = [] + if host: + prefix.extend(["ssh", "%s@%s" % (user, host)]) + return prefix + +def copy_to_publishdir(variables, dist, section, arch=None, quiet=False): + if not arch: + destdir = os.path.join(variables["publish_dir"], dist, section) + else: + destdir = os.path.join(variables["publish_dir"], dist, section, arch) + remote_destdir = destdir + if variables["publish_host"]: + remote_destdir = (("%(publish_user)s@%(publish_host)s:" % variables) + + remote_destdir) + runremote = prefix_for_remote_command(variables["publish_user"], + variables["publish_host"]) + + call(cmdexpand("@runremote mkdir --parents $destdir", + runremote=runremote, destdir=destdir, **variables)) + rsync_flags = [] + if variables["publish_remove_old_packages"]: + rsync_flags.append("--delete") + if quiet: + rsync_flags.append("--quiet") + call(cmdexpand("rsync @rsync_flags -r --perms --times --omit-dir-times" + " $cachedir/ $remote_destdir/", + rsync_flags=rsync_flags, remote_destdir=remote_destdir, + **variables)) + +
--- a/treepkg/readconfig.py Wed Jul 28 08:35:07 2010 +0000 +++ b/treepkg/readconfig.py Fri Aug 06 13:28:47 2010 +0000 @@ -9,6 +9,7 @@ import sys import shlex +import os.path from ConfigParser import SafeConfigParser, NoOptionError import util @@ -78,12 +79,16 @@ ("changelog_msg_template", str, "Update to revision %(revision)s"), ("git_branch", str,""), ("git_url", str,""), + ("os", str, ""), ("builder_cls",str,"PBuilder") ] treepkg_desc = [ ("check_interval", int), "instructions_file", + ("name", str, ""), + ("treepkg_dir", str, None), + ("tracks_dir", str, None) ] @@ -122,6 +127,9 @@ configuration of the tree packager. The packagers list contains one dict with the configuratiin for each packager. """ + if not os.path.exists(filename): + print >>sys.stderr, "Config file %s does not exist" % filename + sys.exit(2) parser = SafeConfigParser() parser.read([filename]) @@ -133,8 +141,8 @@ packager_class = parser.get(section, "packager_class", vars=vars) module = packager.import_packager_module(packager_class) desc = packager_desc + module.PackageTrack.extra_config_desc - packager_options = (read_config_section(parser, section, desc, - defaults=vars)) + packager_options = read_config_section(parser, section, desc, + defaults=vars) if not packager_options.get("svn_url") \ and not packager_options.get('git_url'): print >>sys.stderr, "Missing repository URL in section %r" \ @@ -145,8 +153,7 @@ print >>sys.stderr, \ "Warning: git_url in section %r will be ignored" \ % (section) - packagers.append(read_config_section(parser, section, desc, - defaults=vars)) + packagers.append(packager_options) # main config treepkg = read_config_section(parser, "treepkg", treepkg_desc)
--- a/treepkg/util.py Wed Jul 28 08:35:07 2010 +0000 +++ b/treepkg/util.py Fri Aug 06 13:28:47 2010 +0000 @@ -13,6 +13,8 @@ import shutil import fnmatch import pwd +import os.path +import md5 import run @@ -156,3 +158,19 @@ def getuser(): """Returns the login name of the current user owning the proccess""" return pwd.getpwuid(os.getuid())[0] + +def md5sum(filename): + """ calculates the md5sum of a file """ + if not os.path.isfile(filename): + raise RuntimeError("Could not create md5sum. File not found: %s" + % filename) + f = file(filename, 'rb') + m = md5.new() + while True: + d = f.read(8096) + if not d: + break + m.update(d) + f.close() + return m.hexdigest() +