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