]>
Commit | Line | Data |
---|---|---|
03861ee2 ER |
1 | #!/usr/bin/env python |
2 | # vi: encoding=utf-8 ts=8 sts=4 sw=4 et | |
3 | ||
2ec96333 JR |
4 | from __future__ import print_function |
5 | ||
c88699b9 | 6 | import sys, os, re |
66a58132 | 7 | import getopt |
03861ee2 ER |
8 | import subprocess |
9 | sys.path.insert(0, os.environ['HOME']+'/pld-ftp-admin/modules') | |
10 | import ftptree | |
11 | from common import checkdir | |
12 | import ftpio | |
13 | ||
66a58132 | 14 | try: |
7dd6fac6 | 15 | opts, args = getopt.getopt(sys.argv[1:], 'qsdo:', [ "quiet" ]) |
66a58132 | 16 | except getopt.GetoptError: |
2ec96333 JR |
17 | print("ERR: options error", file=sys.stderr) |
18 | print("rpmlint.py tree package1 [package2...]", file=sys.stderr) | |
66a58132 ER |
19 | sys.exit(1) |
20 | ||
21 | quiet = False | |
413b382d | 22 | show = False |
7dd6fac6 | 23 | debugfiles = False |
cb260c8e | 24 | outstream = sys.stdout |
66a58132 ER |
25 | for o, a in opts: |
26 | if o == "-q" or o == "--quiet": | |
27 | quiet = True | |
413b382d ER |
28 | if o == "-s": |
29 | show = True | |
7dd6fac6 ER |
30 | if o == "-d": |
31 | debugfiles = True | |
cb260c8e ER |
32 | if o == "-o": |
33 | outstream = open(a, 'w') | |
66a58132 ER |
34 | |
35 | if len(args) < 1: | |
2ec96333 JR |
36 | print("ERR: missing tree name", file=sys.stderr) |
37 | print("rpmlint.py tree package1 [package2...]", file=sys.stderr) | |
03861ee2 ER |
38 | sys.exit(1) |
39 | ||
66a58132 ER |
40 | treename = args[0] |
41 | packages = args[1:] | |
42 | ||
43 | checkdir(treename) | |
03861ee2 ER |
44 | |
45 | ftpio.connect('rpmlint') | |
46 | ||
66a58132 | 47 | if not ftpio.lock(treename, True): |
2ec96333 | 48 | print("ERR: %s tree already locked" % treename, file=sys.stderr) |
03861ee2 ER |
49 | sys.exit(1) |
50 | ||
51 | files = [] | |
52 | try: | |
66a58132 | 53 | if len(packages) < 1: |
7e771c27 ER |
54 | loadall = True |
55 | else: | |
56 | loadall = False | |
57 | ||
58 | # if no files specified, grab whole tree contents | |
66a58132 | 59 | tree = ftptree.FtpTree(treename, loadall = loadall) |
413b382d | 60 | tree.do_checkbuild = False |
7e771c27 ER |
61 | if loadall: |
62 | # this is hack, should be a param, not access private .loadedpkgs element | |
63 | tree.mark4moving(tree.loadedpkgs) | |
64 | else: | |
66a58132 | 65 | tree.mark4moving(packages) |
7dd6fac6 | 66 | files = tree.rpmfiles(debugfiles = debugfiles, sourcefiles = False) |
03861ee2 | 67 | |
9c170c61 | 68 | except (ftptree.SomeError, KeyboardInterrupt) as e: |
03861ee2 | 69 | # In case of problems we need to unlock the tree before exiting |
66a58132 | 70 | ftpio.unlock(treename) |
03861ee2 ER |
71 | sys.exit(1) |
72 | ||
66a58132 | 73 | ftpio.unlock(treename) |
03861ee2 | 74 | |
8d0f868f ER |
75 | class LintPkg: |
76 | def __init__(self, cachedir): | |
cb260c8e ER |
77 | self.outstream = sys.stdout |
78 | ||
8d0f868f ER |
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 | ||
2a153648 ER |
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 | ||
8d0f868f ER |
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 | ||
413b382d | 100 | def get_stats(self, file): |
8d0f868f | 101 | cachefile = self.cachefile(file) |
f809cdb0 ER |
102 | if not os.path.exists(cachefile): |
103 | return None | |
8d0f868f ER |
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: | |
413b382d | 109 | return None |
8d0f868f | 110 | |
413b382d ER |
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'] | |
8d0f868f ER |
129 | return True |
130 | ||
131 | def print_stats(self, file = None): | |
132 | if file: | |
133 | (dirname, filename) = os.path.split(file) | |
2ec96333 | 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) |
8d0f868f | 135 | else: |
2ec96333 | 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) |
8d0f868f ER |
137 | sys.stdout.flush() |
138 | ||
f809cdb0 | 139 | def cat(self, file): |
2ec96333 | 140 | print("".join(open(file, 'r').readlines()), file=self.outstream) |
f809cdb0 | 141 | |
413b382d ER |
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) | |
f809cdb0 | 148 | if not os.path.exists(cachefile): |
2ec96333 | 149 | print("MISSING: report: %s" % file, file=self.outstream) |
f809cdb0 | 150 | |
413b382d | 151 | if m['errors'] > 0 or m['warnings'] > 0: |
cb260c8e | 152 | (dirname, filename) = os.path.split(file) |
2ec96333 | 153 | print("rpmlint: %s" % filename, file=self.outstream) |
f809cdb0 | 154 | self.cat(cachefile) |
413b382d | 155 | |
8d0f868f ER |
156 | def rpmlint(self, file): |
157 | cachefile = self.cachefile(file) | |
158 | ||
159 | rc = None | |
2a153648 ER |
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): | |
8d0f868f ER |
163 | cmd = [self._rpmlint, file] |
164 | outfd = open(cachefile, 'w') | |
165 | try: | |
ffcd80d9 ER |
166 | env = {'TZ': 'GMT'} |
167 | rc = subprocess.call(cmd, stdin = subprocess.PIPE, stdout = outfd, stderr = outfd, env = env, close_fds = True) | |
8d0f868f ER |
168 | except KeyboardInterrupt: |
169 | os.unlink(cachefile) | |
170 | raise | |
171 | outfd.close() | |
172 | if not self.update_stats(file): | |
f809cdb0 ER |
173 | # update failed, dump cache and remove it |
174 | self.cat(cachefile) | |
8095749f | 175 | os.unlink(cachefile) |
8d0f868f ER |
176 | rc = 1 |
177 | return rc == 0 | |
03861ee2 | 178 | |
ec7915aa ER |
179 | try: |
180 | lock = 'rpmlint:'+treename | |
181 | if not ftpio.lock(lock, True): | |
2ec96333 | 182 | print("ERR: %s tree already locked for rpmlint" % treename, file=sys.stderr) |
ec7915aa | 183 | sys.exit(1) |
f0cc35ee | 184 | |
66a58132 | 185 | if not quiet: |
2ec96333 | 186 | print("rpmlint of %d files from %d packages" % (len(files), len(tree.loadedpkgs)), file=outstream) |
ec7915aa | 187 | lint = LintPkg("~/tmp/rpmlint") |
cb260c8e | 188 | lint.outstream = outstream |
ec7915aa ER |
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) | |
8d0f868f | 195 | |
ec7915aa ER |
196 | if not quiet: |
197 | lint.print_stats() | |
f0cc35ee | 198 | |
ec7915aa | 199 | ftpio.unlock(lock) |
cb260c8e | 200 | except (Exception, KeyboardInterrupt): |
ec7915aa ER |
201 | ftpio.unlock(lock) |
202 | raise |