]> TLD Linux GIT Repositories - tld-ftp-admin.git/blob - bin/pfa-lintpkg
- raw from PLD
[tld-ftp-admin.git] / bin / pfa-lintpkg
1 #!/usr/bin/env python
2 # vi: encoding=utf-8 ts=8 sts=4 sw=4 et
3
4 import sys, os, re
5 import getopt
6 import subprocess
7 sys.path.insert(0, os.environ['HOME']+'/pld-ftp-admin/modules')
8 import ftptree
9 from common import checkdir
10 import ftpio
11
12 try:
13     opts, args = getopt.getopt(sys.argv[1:], 'qsdo:', [ "quiet" ])
14 except getopt.GetoptError:
15     print >>sys.stderr, "ERR: options error"
16     print >>sys.stderr, "rpmlint.py tree package1 [package2...]"
17     sys.exit(1)
18
19 quiet = False
20 show = False
21 debugfiles = False
22 outstream = sys.stdout
23 for o, a in opts:
24     if o == "-q" or o == "--quiet":
25         quiet = True
26     if o == "-s":
27         show = True
28     if o == "-d":
29         debugfiles = True
30     if o == "-o":
31         outstream = open(a, 'w')
32
33 if len(args) < 1:
34     print >>sys.stderr, "ERR: missing tree name"
35     print >>sys.stderr, "rpmlint.py tree package1 [package2...]"
36     sys.exit(1)
37
38 treename = args[0]
39 packages = args[1:]
40
41 checkdir(treename)
42
43 ftpio.connect('rpmlint')
44
45 if not ftpio.lock(treename, True):
46     print >>sys.stderr, "ERR: %s tree already locked" % treename
47     sys.exit(1)
48
49 files = []
50 try:
51     if len(packages) < 1:
52         loadall = True
53     else:
54         loadall = False
55
56     # if no files specified, grab whole tree contents
57     tree = ftptree.FtpTree(treename, loadall = loadall)
58     tree.do_checkbuild = False
59     if loadall:
60         # this is hack, should be a param, not access private .loadedpkgs element
61         tree.mark4moving(tree.loadedpkgs)
62     else:
63         tree.mark4moving(packages)
64     files = tree.rpmfiles(debugfiles = debugfiles, sourcefiles = False)
65
66 except (ftptree.SomeError, KeyboardInterrupt), e:
67     # In case of problems we need to unlock the tree before exiting
68     ftpio.unlock(treename)
69     sys.exit(1)
70
71 ftpio.unlock(treename)
72
73 class LintPkg:
74     def __init__(self, cachedir):
75         self.outstream = sys.stdout
76
77         # for rpmlint stats
78         self.packages = self.specfiles = self.errors = self.warnings = 0
79         # 1 packages and 0 specfiles checked; 0 errors, 0 warnings.
80         self.lintre = re.compile('(?P<packages>\d+) packages and (?P<specfiles>\d+) specfiles checked; (?P<errors>\d+) errors, (?P<warnings>\d+) warnings.')
81
82         self._rpmlint = '/usr/bin/rpmlint'
83
84         # mtime, which invalidates all caches
85         self.mtime = None
86         rpmlintrc = os.path.expanduser("~/.config/rpmlint")
87         if os.path.exists(rpmlintrc):
88             self.mtime = os.stat(rpmlintrc).st_mtime
89
90         self.cachedir = os.path.expanduser(cachedir)
91         if not os.path.isdir(self.cachedir):
92             os.makedirs(self.cachedir)
93
94     def cachefile(self, file):
95         (dirname, filename) = os.path.split(file)
96         return os.path.join(self.cachedir, filename+'.txt')
97
98     def get_stats(self, file):
99         cachefile = self.cachefile(file)
100         if not os.path.exists(cachefile):
101             return None
102
103         # show last line (that contains status)
104         l = (open(cachefile, 'r').readlines())[-1]
105         m = self.lintre.match(l)
106         if not m:
107             return None
108
109         return {
110             'packages': int(m.group('packages')),
111             'specfiles': int(m.group('specfiles')),
112             'errors': int(m.group('errors')),
113             'warnings': int(m.group('warnings')),
114         }
115
116     """
117     update stats from cachefile
118     """
119     def update_stats(self, file):
120         m = self.get_stats(file)
121         if not m:
122             return False
123         self.packages += m['packages']
124         self.specfiles += m['specfiles']
125         self.errors += m['errors']
126         self.warnings += m['warnings']
127         return True
128
129     def print_stats(self, file = None):
130         if file:
131             (dirname, filename) = os.path.split(file)
132             print >>self.outstream, "\r\033[0K%d packages and %d specfiles checked; %d errors, %d warnings. [%s]" % (self.packages, self.specfiles, self.errors, self.warnings, filename),
133         else:
134             print >>self.outstream, "\r\033[0K%d packages and %d specfiles checked; %d errors, %d warnings." % (self.packages, self.specfiles, self.errors, self.warnings)
135         sys.stdout.flush()
136
137     def cat(self, file):
138         print >>self.outstream, "".join(open(file, 'r').readlines())
139
140     def show_results(self, file):
141         m = self.get_stats(file)
142         if not m:
143             return False
144
145         cachefile = self.cachefile(file)
146         if not os.path.exists(cachefile):
147             print >>self.outsteram, "MISSING: report: %s" % file
148
149         if m['errors'] > 0 or m['warnings'] > 0:
150             (dirname, filename) = os.path.split(file)
151             print >>self.outstream, "rpmlint: %s" % filename
152             self.cat(cachefile)
153
154     def rpmlint(self, file):
155         cachefile = self.cachefile(file)
156
157         rc = None
158         if not os.path.exists(cachefile) \
159             or os.stat(file).st_mtime > os.stat(cachefile).st_mtime \
160             or (self.mtime and self.mtime > os.stat(cachefile).st_mtime):
161             cmd = [self._rpmlint, file]
162             outfd = open(cachefile, 'w')
163             try:
164                 env = {'TZ': 'GMT'}
165                 rc = subprocess.call(cmd, stdin = subprocess.PIPE, stdout = outfd, stderr = outfd, env = env, close_fds = True)
166             except KeyboardInterrupt:
167                 os.unlink(cachefile)
168                 raise
169             outfd.close()
170         if not self.update_stats(file):
171             # update failed, dump cache and remove it
172             self.cat(cachefile)
173             os.unlink(cachefile)
174             rc = 1
175         return rc == 0
176
177 try:
178     lock = 'rpmlint:'+treename
179     if not ftpio.lock(lock, True):
180         print >>sys.stderr, "ERR: %s tree already locked for rpmlint" % treename
181         sys.exit(1)
182
183     if not quiet:
184         print >>outstream, "rpmlint of %d files from %d packages" % (len(files), len(tree.loadedpkgs))
185     lint = LintPkg("~/tmp/rpmlint")
186     lint.outstream = outstream
187     for file in files:
188         lint.rpmlint(file)
189         if not quiet:
190             lint.print_stats(file)
191         if show:
192             lint.show_results(file)
193
194     if not quiet:
195         lint.print_stats()
196
197     ftpio.unlock(lock)
198 except (Exception, KeyboardInterrupt):
199     ftpio.unlock(lock)
200     raise