Mercurial > treepkg
comparison contrib/bin/copy-latest-pkgs.py @ 542:dc17b62d3cdd
contrib: Added copy-latest-pkgs.py to copy/hardlink latest
packages from the mill to the next higher attention level.
author | Sascha Teichmann <teichmann@intevation.de> |
---|---|
date | Tue, 11 Jan 2011 17:20:02 +0000 |
parents | |
children | 247a10201cdd |
comparison
equal
deleted
inserted
replaced
541:8b49548aa8d4 | 542:dc17b62d3cdd |
---|---|
1 #!/usr/bin/env python | |
2 # -*- coding: UTF-8 -*- | |
3 # | |
4 # Copyright (C) 2011 by Intevation GmbH | |
5 # Authors: | |
6 # Sascha L. Teichmann <sascha.teichmann@intevation.de> | |
7 # | |
8 # This program is free software under the GPL (>=v2) | |
9 # Read the file COPYING coming with the software for details. | |
10 | |
11 import os | |
12 import re | |
13 import sys | |
14 import subprocess | |
15 import logging | |
16 import traceback | |
17 | |
18 from optparse import OptionParser | |
19 from shutil import copyfile | |
20 | |
21 log = logging.getLogger(__name__) | |
22 log.setLevel(logging.WARNING) | |
23 log.addHandler(logging.StreamHandler(sys.stderr)) | |
24 | |
25 SAEGEWERKER = "saegewerker" | |
26 | |
27 FIELD = re.compile("([a-zA-Z]+):\s*(.+)") | |
28 | |
29 | |
30 class DebCmp(object): | |
31 """Helper class to make deb files comparable | |
32 by there versions. | |
33 """ | |
34 | |
35 def __init__(self, version, path): | |
36 self.version = version | |
37 self.path = path | |
38 | |
39 def __cmp__(self, other): | |
40 if self.version == other.version: | |
41 return 0 | |
42 if (subprocess.call([ | |
43 "dpkg", "--compare-versions", | |
44 self.version, "gt", other.version]) == 0): | |
45 return +1 | |
46 if (subprocess.call([ | |
47 "dpkg", "--compare-versions", | |
48 self.version, "lt", other.version]) == 0): | |
49 return -1 | |
50 return 0 | |
51 | |
52 def __str__(self): | |
53 return "version: %s / path: %s" % ( | |
54 self.version, | |
55 self.path) | |
56 | |
57 def deb_info(deb, fields=["Package", "Version"]): | |
58 """Extract some meta info from a deb file.""" | |
59 po = subprocess.Popen( | |
60 ["dpkg-deb", "-f", deb] + fields, | |
61 stdout=subprocess.PIPE) | |
62 out = po.communicate()[0] | |
63 return dict([m.groups() | |
64 for m in map(FIELD.match, out.splitlines()) if m]) | |
65 | |
66 | |
67 def copy_pkgs(src, dst, options): | |
68 | |
69 archs = {} | |
70 | |
71 for arch in os.listdir(src): | |
72 if arch == 'source': continue | |
73 arch_dir = os.path.join(src, arch) | |
74 if not os.path.isdir(arch_dir): continue | |
75 log.debug("found arch: '%s'" % arch) | |
76 | |
77 tracks = {} | |
78 | |
79 for track in os.listdir(arch_dir): | |
80 track_dir = os.path.join(arch_dir, track) | |
81 if not os.path.isdir(track_dir): continue | |
82 | |
83 packages = {} | |
84 | |
85 log.debug("track dir: '%s'" % track_dir) | |
86 for f in os.listdir(track_dir): | |
87 if not f.endswith(".deb"): continue | |
88 deb_path = os.path.join(track_dir, f) | |
89 if not os.path.isfile(deb_path): continue | |
90 | |
91 info = deb_info(deb_path) | |
92 deb_cmp = DebCmp(info['Version'], deb_path) | |
93 | |
94 packages.setdefault(info['Package'], []).append(deb_cmp) | |
95 | |
96 tracks[track] =[max(debs) for debs in packages.itervalues()] | |
97 | |
98 archs[arch] = tracks | |
99 | |
100 copy = options.no_hardlinks and copyfile or os.link | |
101 action = options.no_hardlinks and "copy" or "link" | |
102 | |
103 for arch, tracks in archs.iteritems(): | |
104 log.debug("writing arch '%s'" % arch) | |
105 for track, debs in tracks.iteritems(): | |
106 log.debug(" writing track '%s'" % track) | |
107 dst_dir = os.path.join(dst, arch, track) | |
108 if not os.path.exists(dst_dir): | |
109 try: | |
110 os.makedirs(dst_dir) | |
111 except: | |
112 log.warn(traceback.format_exc()) | |
113 continue | |
114 | |
115 for deb in debs: | |
116 src_path = deb.path | |
117 dst_path = os.path.join(dst_dir, os.path.basename(src_path)) | |
118 log.info(" %s '%s' -> '%s'" % (action, src_path, dst_path)) | |
119 if os.path.isfile(dst_path): | |
120 try: os.remove(dst_path) | |
121 except: log.warn(traceback.format_exc()); continue | |
122 try: copy(src_path, dst_path) | |
123 except: log.warn(traceback.format_exc()) | |
124 | |
125 | |
126 def main(): | |
127 usage = "usage: %prog [options] src-dir dst-dir" | |
128 parser = OptionParser(usage=usage) | |
129 parser.add_option( | |
130 "-v", "--verbose", action="store_true", | |
131 dest="verbose", | |
132 help="verbose output") | |
133 parser.add_option( | |
134 "-d", "--dry-run", action="store_true", | |
135 dest="dry_run", default=False, | |
136 help="don't copy the deb files") | |
137 parser.add_option( | |
138 "-n", "--no-saegewerker", action="store_true", | |
139 dest="no_saegewerker", default=False, | |
140 help="Don't force run as '%s'" % SAEGEWERKER) | |
141 parser.add_option( | |
142 "-l", "--no-hardlinks", action="store_false", | |
143 dest="no_hardlinks", default=False, | |
144 help="copy files instead of hard linking") | |
145 | |
146 options, args = parser.parse_args() | |
147 | |
148 if len(args) < 2: | |
149 log.error("need at least two arguments") | |
150 sys.exit(1) | |
151 | |
152 src, dst = args[0], args[1] | |
153 | |
154 for d in (src, dst): | |
155 if not os.path.isdir(d): | |
156 log.error("'%s' is not a directory." % d) | |
157 sys.exit(1) | |
158 | |
159 if options.verbose: log.setLevel(logging.INFO) | |
160 | |
161 if not options.no_saegewerker and os.environ['USER'] != SAEGEWERKER: | |
162 log.error("Need to run as '%s'" % SAEGEWERKER) | |
163 sys.exit(1) | |
164 | |
165 copy_pkgs(src, dst, options) | |
166 | |
167 if __name__ == '__main__': | |
168 main() |