# HG changeset patch # User Sascha Teichmann # Date 1294766402 0 # Node ID dc17b62d3cddb86ff3800a452e31b44b2a6a4b45 # Parent 8b49548aa8d4b9b39e0673fc7a31bebcf71b67bd contrib: Added copy-latest-pkgs.py to copy/hardlink latest packages from the mill to the next higher attention level. diff -r 8b49548aa8d4 -r dc17b62d3cdd contrib/bin/README --- a/contrib/bin/README Tue Jan 11 16:27:02 2011 +0000 +++ b/contrib/bin/README Tue Jan 11 17:20:02 2011 +0000 @@ -17,3 +17,19 @@ -v, --verbose verbose output -d, --dry-run don't remove the old deb files -k KEEP, --keep=KEEP number of files to keep. Default: 3 + + +copy-latest-pkgs.py +------------------- +Copies/Hardlinks deb files from snapshots to experimental. +It performs a check if you run it as user 'saegewerker'. +By default the files are hardlinked. + +Usage: copy-latest-pkgs.py [options] src-dir dst-dir + +Options: + -h, --help show this help message and exit + -v, --verbose verbose output + -d, --dry-run don't copy the deb files + -n, --no-saegewerker Don't force run as 'saegewerker' + -l, --no-hardlinks copy files instead of hard linking diff -r 8b49548aa8d4 -r dc17b62d3cdd contrib/bin/copy-latest-pkgs.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/contrib/bin/copy-latest-pkgs.py Tue Jan 11 17:20:02 2011 +0000 @@ -0,0 +1,168 @@ +#!/usr/bin/env python +# -*- coding: UTF-8 -*- +# +# Copyright (C) 2011 by Intevation GmbH +# Authors: +# Sascha L. Teichmann +# +# This program is free software under the GPL (>=v2) +# Read the file COPYING coming with the software for details. + +import os +import re +import sys +import subprocess +import logging +import traceback + +from optparse import OptionParser +from shutil import copyfile + +log = logging.getLogger(__name__) +log.setLevel(logging.WARNING) +log.addHandler(logging.StreamHandler(sys.stderr)) + +SAEGEWERKER = "saegewerker" + +FIELD = re.compile("([a-zA-Z]+):\s*(.+)") + + +class DebCmp(object): + """Helper class to make deb files comparable + by there versions. + """ + + def __init__(self, version, path): + self.version = version + self.path = path + + def __cmp__(self, other): + if self.version == other.version: + return 0 + if (subprocess.call([ + "dpkg", "--compare-versions", + self.version, "gt", other.version]) == 0): + return +1 + if (subprocess.call([ + "dpkg", "--compare-versions", + self.version, "lt", other.version]) == 0): + return -1 + return 0 + + def __str__(self): + return "version: %s / path: %s" % ( + self.version, + self.path) + +def deb_info(deb, fields=["Package", "Version"]): + """Extract some meta info from a deb file.""" + po = subprocess.Popen( + ["dpkg-deb", "-f", deb] + fields, + stdout=subprocess.PIPE) + out = po.communicate()[0] + return dict([m.groups() + for m in map(FIELD.match, out.splitlines()) if m]) + + +def copy_pkgs(src, dst, options): + + archs = {} + + for arch in os.listdir(src): + if arch == 'source': continue + arch_dir = os.path.join(src, arch) + if not os.path.isdir(arch_dir): continue + log.debug("found arch: '%s'" % arch) + + tracks = {} + + for track in os.listdir(arch_dir): + track_dir = os.path.join(arch_dir, track) + if not os.path.isdir(track_dir): continue + + packages = {} + + log.debug("track dir: '%s'" % track_dir) + for f in os.listdir(track_dir): + if not f.endswith(".deb"): continue + deb_path = os.path.join(track_dir, f) + if not os.path.isfile(deb_path): continue + + info = deb_info(deb_path) + deb_cmp = DebCmp(info['Version'], deb_path) + + packages.setdefault(info['Package'], []).append(deb_cmp) + + tracks[track] =[max(debs) for debs in packages.itervalues()] + + archs[arch] = tracks + + copy = options.no_hardlinks and copyfile or os.link + action = options.no_hardlinks and "copy" or "link" + + for arch, tracks in archs.iteritems(): + log.debug("writing arch '%s'" % arch) + for track, debs in tracks.iteritems(): + log.debug(" writing track '%s'" % track) + dst_dir = os.path.join(dst, arch, track) + if not os.path.exists(dst_dir): + try: + os.makedirs(dst_dir) + except: + log.warn(traceback.format_exc()) + continue + + for deb in debs: + src_path = deb.path + dst_path = os.path.join(dst_dir, os.path.basename(src_path)) + log.info(" %s '%s' -> '%s'" % (action, src_path, dst_path)) + if os.path.isfile(dst_path): + try: os.remove(dst_path) + except: log.warn(traceback.format_exc()); continue + try: copy(src_path, dst_path) + except: log.warn(traceback.format_exc()) + + +def main(): + usage = "usage: %prog [options] src-dir dst-dir" + parser = OptionParser(usage=usage) + parser.add_option( + "-v", "--verbose", action="store_true", + dest="verbose", + help="verbose output") + parser.add_option( + "-d", "--dry-run", action="store_true", + dest="dry_run", default=False, + help="don't copy the deb files") + parser.add_option( + "-n", "--no-saegewerker", action="store_true", + dest="no_saegewerker", default=False, + help="Don't force run as '%s'" % SAEGEWERKER) + parser.add_option( + "-l", "--no-hardlinks", action="store_false", + dest="no_hardlinks", default=False, + help="copy files instead of hard linking") + + options, args = parser.parse_args() + + if len(args) < 2: + log.error("need at least two arguments") + sys.exit(1) + + src, dst = args[0], args[1] + + for d in (src, dst): + if not os.path.isdir(d): + log.error("'%s' is not a directory." % d) + sys.exit(1) + + if options.verbose: log.setLevel(logging.INFO) + + if not options.no_saegewerker and os.environ['USER'] != SAEGEWERKER: + log.error("Need to run as '%s'" % SAEGEWERKER) + sys.exit(1) + + copy_pkgs(src, dst, options) + +if __name__ == '__main__': + main()