comparison contrib/bin/delete-old-debs.py @ 535:fb7a900a649f

contrib: Added script to remove old deb files from directories.
author Sascha Teichmann <teichmann@intevation.de>
date Sat, 08 Jan 2011 12:57:07 +0000
parents
children 8a61185a3357
comparison
equal deleted inserted replaced
534:09fcac0bdc25 535:fb7a900a649f
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 sys
12 import os
13 import re
14
15 import subprocess
16 import logging
17
18 from heapq import nsmallest
19
20 from optparse import OptionParser
21
22 log = logging.getLogger(__name__)
23 log.setLevel(logging.WARNING)
24 log.addHandler(logging.StreamHandler(sys.stderr))
25
26 DEFAULT_KEEP = 3
27
28 FIELD = re.compile("([a-zA-Z]+):\s*(.+)")
29
30 # map rich comparison to 'dpkg --compare-versions'
31 # map == to !=, < to >= and so on to reverse order in heap.
32 RICH_CMP = dict([
33 ("__%s__" % a, lambda se, ot:
34 subprocess.call([
35 "dpkg", "--compare-versions",
36 se.version, b, ot.version]) == 0)
37 for a, b in (("eq", "ne"), ("ne", "eq"),
38 ("lt", "ge"), ("gt", "le"),
39 ("le", "gt"), ("ge", "lt"))])
40
41
42 class DebCmp(object):
43 """Helper class to make deb files comparable
44 by there versions.
45 """
46
47 def __init__(self, version, path):
48 self.version = version
49 self.path = path
50
51 self.__dict__.update(RICH_CMP)
52
53
54 def deb_info(deb, fields=["Package", "Version"]):
55 """Extract some meta info from a deb file."""
56 po = subprocess.Popen(
57 ["dpkg-deb", "-f", deb] + fields,
58 stdout=subprocess.PIPE)
59 out = po.communicate()[0]
60 return dict([m.groups()
61 for m in map(FIELD.match, out.splitlines()) if m])
62
63
64 def oldest_debs(deb_dir, keep=DEFAULT_KEEP):
65 """Given directory containing deb files this function
66 returns the files that are older than the youngest
67 keep-th per package.
68 """
69
70 log.info("scanning dir '%s'" % deb_dir)
71
72 packages = {}
73
74 num = 1
75 for f in os.listdir(deb_dir):
76 if not f.endswith(".deb"): continue
77 deb = os.path.join(deb_dir, f)
78 if not os.path.isfile(deb): continue
79 info = deb_info(deb)
80 packages.setdefault(info['Package'], []).append(
81 DebCmp(info['Version'], deb))
82 if (num % 10) == 0:
83 log.info("%d debs found" % (num-1))
84 num += 1
85
86 if log.isEnabledFor(logging.INFO):
87 log.info("%d debs found" % (num-1))
88 log.info("number packages: %s" % len(packages))
89
90 for package, debs in packages.iteritems():
91 if len(debs) > keep:
92 # full sorting is not required
93 stay = frozenset([d.path for d in nsmallest(keep, debs)])
94
95 for deb in debs:
96 if deb.path not in stay:
97 yield deb.path
98
99
100 def main():
101 usage = "usage: %prog [options] dir ..."
102 parser = OptionParser(usage=usage)
103 parser.add_option(
104 "-v", "--verbose", action="store_true",
105 dest="verbose",
106 help="verbose output")
107 parser.add_option(
108 "-d", "--dry-run", action="store_true",
109 dest="dry_run",
110 help="don't remove the old deb files")
111 parser.add_option(
112 "-k", "--keep", action="store",
113 dest="keep", type="int", default=DEFAULT_KEEP,
114 help="number of files to keep. Default: %d" % DEFAULT_KEEP)
115
116 options, args = parser.parse_args()
117
118 remove = options.dry_run and (lambda x: None) or os.remove
119 keep = max(1, options.keep)
120 if options.verbose: log.setLevel(logging.INFO)
121
122 for deb_dir in args:
123
124 if not os.path.isdir(deb_dir):
125 log.warn("'%s' is not a directory" % deb_dir)
126 continue
127
128 for deb in oldest_debs(deb_dir, keep):
129 log.debug("remove '%s'" % deb)
130 remove(deb)
131 changes = deb.path[:-3] + "changes"
132 if os.path.isfile(changes):
133 log.debug("remove '%s'" % changes)
134 remove(changes)
135
136
137 if __name__ == "__main__":
138 main()
This site is hosted by Intevation GmbH (Datenschutzerklärung und Impressum | Privacy Policy and Imprint)