#!/usr/bin/env python3 # vi: encoding=utf-8 ts=8 sts=4 sw=4 et from __future__ import print_function import sys, os, re import getopt import subprocess sys.path.insert(0, os.environ['HOME']+'/tld-ftp-admin/modules') import ftptree from common import checkdir import ftpio try: opts, args = getopt.getopt(sys.argv[1:], 'qsdo:', [ "quiet" ]) except getopt.GetoptError: print("ERR: options error", file=sys.stderr) print("rpmlint.py tree package1 [package2...]", file=sys.stderr) sys.exit(1) quiet = False show = False debugfiles = False outstream = sys.stdout for o, a in opts: if o == "-q" or o == "--quiet": quiet = True if o == "-s": show = True if o == "-d": debugfiles = True if o == "-o": outstream = open(a, 'w') if len(args) < 1: print("ERR: missing tree name", file=sys.stderr) print("rpmlint.py tree package1 [package2...]", file=sys.stderr) sys.exit(1) treename = args[0] packages = args[1:] checkdir(treename) ftpio.connect('rpmlint') if not ftpio.lock(treename, True): print("ERR: %s tree already locked" % treename, file=sys.stderr) sys.exit(1) files = [] try: if len(packages) < 1: loadall = True else: loadall = False # if no files specified, grab whole tree contents tree = ftptree.FtpTree(treename, loadall = loadall) tree.do_checkbuild = False if loadall: # this is hack, should be a param, not access private .loadedpkgs element tree.mark4moving(tree.loadedpkgs) else: tree.mark4moving(packages) files = tree.rpmfiles(debugfiles = debugfiles, sourcefiles = False) except (ftptree.SomeError, KeyboardInterrupt) as e: # In case of problems we need to unlock the tree before exiting ftpio.unlock(treename) sys.exit(1) ftpio.unlock(treename) class LintPkg: def __init__(self, cachedir): self.outstream = sys.stdout # for rpmlint stats self.packages = self.specfiles = self.errors = self.warnings = 0 # 1 packages and 0 specfiles checked; 0 errors, 0 warnings. self.lintre = re.compile('(?P\d+) packages and (?P\d+) specfiles checked; (?P\d+) errors, (?P\d+) warnings.') self._rpmlint = '/usr/bin/rpmlint' # mtime, which invalidates all caches self.mtime = None rpmlintrc = os.path.expanduser("~/.config/rpmlint") if os.path.exists(rpmlintrc): self.mtime = os.stat(rpmlintrc).st_mtime self.cachedir = os.path.expanduser(cachedir) if not os.path.isdir(self.cachedir): os.makedirs(self.cachedir) def cachefile(self, file): (dirname, filename) = os.path.split(file) return os.path.join(self.cachedir, filename+'.txt') def get_stats(self, file): cachefile = self.cachefile(file) if not os.path.exists(cachefile) or os.path.getsize(cachefile) <= 0: return None # show last line (that contains status) l = (open(cachefile, 'r').readlines())[-1] m = self.lintre.match(l) if not m: return None return { 'packages': int(m.group('packages')), 'specfiles': int(m.group('specfiles')), 'errors': int(m.group('errors')), 'warnings': int(m.group('warnings')), } """ update stats from cachefile """ def update_stats(self, file): m = self.get_stats(file) if not m: return False self.packages += m['packages'] self.specfiles += m['specfiles'] self.errors += m['errors'] self.warnings += m['warnings'] return True def print_stats(self, file = None): if file: (dirname, filename) = os.path.split(file) 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) else: 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) sys.stdout.flush() def cat(self, file): print("".join(open(file, 'r').readlines()), file=self.outstream) def show_results(self, file): m = self.get_stats(file) if not m: return False cachefile = self.cachefile(file) if not os.path.exists(cachefile): print("MISSING: report: %s" % file, file=self.outstream) if m['errors'] > 0 or m['warnings'] > 0: (dirname, filename) = os.path.split(file) print("rpmlint: %s" % filename, file=self.outstream) self.cat(cachefile) def rpmlint(self, file): cachefile = self.cachefile(file) rc = None if not os.path.exists(cachefile) \ or os.stat(file).st_mtime > os.stat(cachefile).st_mtime \ or (self.mtime and self.mtime > os.stat(cachefile).st_mtime): cmd = [self._rpmlint, file] outfd = open(cachefile, 'w') try: env = {'TZ': 'GMT'} rc = subprocess.call(cmd, stdin = subprocess.PIPE, stdout = outfd, stderr = outfd, env = env, close_fds = True) except KeyboardInterrupt: os.unlink(cachefile) raise outfd.close() if not self.update_stats(file): # update failed, dump cache and remove it self.cat(cachefile) os.unlink(cachefile) rc = 1 return rc == 0 try: lock = 'rpmlint:'+treename if not ftpio.lock(lock, True): print("ERR: %s tree already locked for rpmlint" % treename, file=sys.stderr) sys.exit(1) if not quiet: print("rpmlint of %d files from %d packages" % (len(files), len(tree.loadedpkgs)), file=outstream) lint = LintPkg("~/tmp/rpmlint") lint.outstream = outstream for file in files: lint.rpmlint(file) if not quiet: lint.print_stats(file) if show: lint.show_results(file) if not quiet: lint.print_stats() ftpio.unlock(lock) except (Exception, KeyboardInterrupt): ftpio.unlock(lock) raise