class Lint(object): """ Generic object handling the basic rpmlint operations """ def __init__(self, options): # initialize configuration self.checks = {} self.options = options self.packages_checked = 0 self.specfiles_checked = 0 if options['config']: self.config = Config(options['config']) else: self.config = Config() self._load_rpmlintrc() if options['verbose']: self.config.info = options['verbose'] if options['strict']: self.config.strict = options['strict'] if options['permissive']: self.config.permissive = options['permissive'] if not self.config.configuration['ExtractDir']: self.config.configuration['ExtractDir'] = gettempdir() # initialize output buffer self.output = Filter(self.config) # preload the check list self.load_checks() def run(self): retcode = 0 # if we just want to print config, do so and leave if self.options['print_config']: self.print_config() return retcode # just explain the error and abort too if self.options['explain']: self.print_explanation(self.options['explain']) return retcode # if there are installed arguments just load them up as extra # items to the rpmfile option if self.options['installed']: self.validate_installed_packages( self._load_installed_rpms(self.options['installed'])) # if no exclusive option is passed then just loop over all the # arguments that are supposed to be either rpm or spec files self.validate_files(self.options['rpmfile']) self._print_header() print(self.output.print_results(self.output.results)) quit_color = Color.Bold if self.output.printed_messages['W'] > 0: quit_color = Color.Yellow if self.output.badness_threshold > 0 and self.output.score > self.output.badness_threshold: msg = string_center( f'Badness {self.output.score} exceeeds threshold {self.output.badness_threshold}, aborting.', '-') print(f'{Color.Red}{msg}{Color.Reset}') quit_color = Color.Red retcode = 66 elif self.output.printed_messages[ 'E'] > 0 and not self.config.permissive: quit_color = Color.Red retcode = 64 msg = string_center( '{} packages and {} specfiles checked; {} errors, {} warnings'. format(self.packages_checked, self.specfiles_checked, self.output.printed_messages['E'], self.output.printed_messages['W']), '=') print(f'{quit_color}{msg}{Color.Reset}') return retcode def _load_installed_rpms(self, packages): existing_packages = [] for name in packages: pkg = getInstalledPkgs(name) if pkg: existing_packages.extend(pkg) else: print_warning( f'(none): E: there is no installed rpm "{name}".') return existing_packages def _load_rpmlintrc(self): """ Load rpmlintrc from argument or load up from folder """ if self.options['rpmlintrc']: self.config.load_rpmlintrc(self.options['rpmlintrc']) else: # load only from the same folder specname.rpmlintrc or specname-rpmlintrc # do this only in a case where there is one folder parameter or one file # to avoid multiple folders handling rpmlintrc = [] if not len(self.options['rpmfile']) == 1: return pkg = self.options['rpmfile'][0] if pkg.is_file(): pkg = pkg.parent rpmlintrc += sorted(pkg.glob('*.rpmlintrc')) rpmlintrc += sorted(pkg.glob('*-rpmlintrc')) if len(rpmlintrc) > 1: # multiple rpmlintrcs are highly undesirable print_warning( 'There are multiple items to be loaded for rpmlintrc, ignoring them: {}.' .format(' '.join(map(str, rpmlintrc)))) elif len(rpmlintrc) == 1: self.options['rpmlintrc'] = rpmlintrc[0] self.config.load_rpmlintrc(rpmlintrc[0]) def _print_header(self): """ Print out header information about the state of the rpmlint prior printing out the check report. """ intro = string_center('rpmlint session starts', '=') print(f'{Color.Bold}{intro}{Color.Reset}') print(f'rpmlint: {__version__}') print(f'configuration:') for config in self.config.conf_files: print(f' {config}') if self.options['rpmlintrc']: rpmlintrc = self.options['rpmlintrc'] print(f'rpmlintrc: {rpmlintrc}') no_checks = len(self.config.configuration['Checks']) no_pkgs = len(self.options['installed']) + len(self.options['rpmfile']) print( f'{Color.Bold}checks: {no_checks}, packages: {no_pkgs}{Color.Reset}' ) print('') print('') def validate_installed_packages(self, packages): for pkg in packages: self.run_checks(pkg) def validate_files(self, files): """ Run all the check for passed file list """ if not files: if self.packages_checked == 0: # print warning only if we didn't process even installed files print_warning( 'There are no files to process nor additional arguments.') print_warning('Nothing to do, aborting.') return # check all elements if they are a folder or a file with proper suffix # and expand everything packages = self._expand_filelist(files) for pkg in packages: self.validate_file(pkg) def _expand_filelist(self, files): packages = [] for pkg in files: if pkg.is_file() and self._check_valid_suffix(pkg): packages.append(pkg) elif pkg.is_dir(): packages.extend(self._expand_filelist(pkg.iterdir())) return packages @staticmethod def _check_valid_suffix(filename): if any(ext == filename.suffix for ext in ['.rpm', '.spm', '.spec']): return True return False def validate_file(self, pname): try: if pname.suffix == '.rpm' or pname.suffix == '.spm': with Pkg(pname, self.config.configuration['ExtractDir']) as pkg: self.run_checks(pkg) elif pname.suffix == '.spec': with FakePkg(pname) as pkg: self.run_spec_checks(pkg) except Exception as e: print_warning(f'(none): E: while reading {pname}: {e}') def run_checks(self, pkg): for checker in self.checks: self.checks[checker].check(pkg) self.packages_checked += 1 def run_spec_checks(self, pkg): for checker in self.checks: self.checks[checker].check_spec(pkg) self.specfiles_checked += 1 def print_config(self): """ Just output the current configuration """ self.config.print_config() def print_explanation(self, messages): """ Print out detailed explanation for the specified messages """ for message in messages: explanation = self.output.get_description(message) if not explanation: explanation = 'Unknown message, please report a bug if the description should be present.\n\n' print(f'{message}:\n{explanation}') def load_checks(self): """ Load all checks based on the config, skipping those already loaded SingletonTM """ for check in self.config.configuration['Checks']: if check in self.checks: continue self.checks[check] = self.load_check(check) def load_check(self, name): """Load a (check) module by its name, unless it is already loaded.""" module = importlib.import_module(f'.{name}', package='rpmlint.checks') klass = getattr(module, name) obj = klass(self.config, self.output) return obj
import glob import os from rpmlint.config import Config from rpmlint.pkg import FakePkg, Pkg def testpath(): return os.environ.get('TESTPATH', os.path.dirname(__file__)) TEST_CONFIG = os.path.join(testpath(), 'configs/test.config') CONFIG = Config(TEST_CONFIG) def get_tested_path(path): return os.path.join(testpath(), path) def get_tested_package(name, testdir): pkg_path = glob.glob(get_tested_path(name) + '-*.rpm')[0] return Pkg(pkg_path, testdir) def get_tested_spec_package(name): pkg_path = glob.glob(get_tested_path(name) + '.spec')[0] return FakePkg(pkg_path)
def test_printing(capsys): cfg = Config() cfg.print_config() out, err = capsys.readouterr() assert not err assert out