| # -*- coding:utf-8 -*- |
| |
| import logging |
| import os |
| |
| from _emerge.Package import Package |
| |
| # import our initialized portage instance |
| from repoman import _not_installed |
| from repoman._portage import portage |
| from repoman.config import load_config |
| |
| |
| class QAData(object): |
| |
| def __init__(self): |
| # Create the main exported data variables |
| self.max_desc_len = None |
| self.allowed_filename_chars = None |
| self.qahelp = None |
| self.qacats = None |
| self.qawarnings = None |
| self.missingvars = None |
| self.allvars = None |
| self.valid_restrict = None |
| self.suspect_rdepend = None |
| self.suspect_virtual = None |
| self.ruby_deprecated = None |
| self.no_exec = None |
| |
| |
| def load_repo_config(self, repopaths, options, valid_versions): |
| '''Load the repository repoman qa_data.yml config |
| |
| @param repopaths: list of strings, The path of the repository being scanned |
| This could be a parent repository using the |
| repoman_masters layout.conf variable |
| ''' |
| # add our base qahelp |
| repository_modules = options.experimental_repository_modules == 'y' |
| if _not_installed: |
| cnfdir = os.path.realpath(os.path.join(os.path.dirname( |
| os.path.dirname(os.path.dirname(__file__))), 'cnf/qa_data')) |
| else: |
| cnfdir = os.path.join(portage.const.EPREFIX or '/', 'usr/share/repoman/qa_data') |
| repomanpaths = [os.path.join(cnfdir, _file_) for _file_ in os.listdir(cnfdir)] |
| logging.debug("QAData: cnfdir: %s, repomanpaths: %s", cnfdir, repomanpaths) |
| if repository_modules: |
| repopaths = [os.path.join(path,'qa_data.yaml') for path in repopaths] |
| elif _not_installed: |
| repopaths = [os.path.realpath(os.path.join(os.path.dirname( |
| os.path.dirname(os.path.dirname(__file__))), |
| 'cnf/repository/qa_data.yaml'))] |
| else: |
| repopaths = [os.path.join(portage.const.EPREFIX or '/', |
| 'usr/share/repoman/repository/qa_data.yaml')] |
| infopaths = repomanpaths + repopaths |
| |
| qadata = load_config(infopaths, None, valid_versions) |
| if qadata == {}: |
| logging.error("QAData: Failed to load a valid 'qa_data.yaml' file at paths: %s", infopaths) |
| return False |
| self.max_desc_len = qadata.get('max_description_length', 80) |
| self.allowed_filename_chars = qadata.get("allowed_filename_chars", "a-zA-Z0-9._-+:") |
| |
| self.qahelp = qadata["qahelp"] |
| logging.debug("qa_help primary keys: %s", sorted(self.qahelp)) |
| |
| self.qacats = [] |
| for x in sorted(self.qahelp): |
| for y in sorted(self.qahelp[x]): |
| self.qacats.append('.'.join([x, y])) |
| self.qacats.sort() |
| |
| self.qawarnings = set(qadata.get('qawarnings', [])) |
| if options.experimental_inherit == 'y': |
| # This is experimental, so it's non-fatal. |
| self.qawarnings.add("inherit.missing") |
| |
| self.missingvars = qadata.get("missingvars", []) |
| logging.debug("QAData: missingvars: %s", self.missingvars) |
| self.allvars = set(x for x in portage.auxdbkeys if not x.startswith("UNUSED_")) |
| self.allvars.update(Package.metadata_keys) |
| self.allvars = sorted(self.allvars) |
| |
| for x in self.missingvars: |
| x += ".missing" |
| if x not in self.qacats: |
| logging.warning('QAData: * missingvars values need to be added to qahelp ("%s")' % x) |
| self.qacats.append(x) |
| self.qawarnings.add(x) |
| |
| self.valid_restrict = frozenset(qadata.get("valid_restrict", [])) |
| |
| self.suspect_rdepend = frozenset(qadata.get("suspect_rdepend", [])) |
| |
| self.suspect_virtual = qadata.get("suspect_virtual", {}) |
| |
| self.ruby_deprecated = frozenset(qadata.get("ruby_deprecated", [])) |
| |
| # file.executable |
| self.no_exec = frozenset(qadata.get("no_exec_files", [])) |
| logging.debug("QAData: completed loading file: %s", repopaths) |
| return True |
| |
| |
| def format_qa_output( |
| formatter, fails, dofull, dofail, options, qawarnings): |
| """Helper function that formats output properly |
| |
| @param formatter: an instance of Formatter |
| @type formatter: Formatter |
| @param fails: dict of qa status failures |
| @type fails: dict |
| @param dofull: Whether to print full results or a summary |
| @type dofull: boolean |
| @param dofail: Whether failure was hard or soft |
| @type dofail: boolean |
| @param options: The command-line options provided to repoman |
| @type options: Namespace |
| @param qawarnings: the set of warning types |
| @type qawarnings: set |
| @return: None (modifies formatter) |
| """ |
| full = options.mode == 'full' |
| # we only want key value pairs where value > 0 |
| for category in sorted(fails): |
| number = len(fails[category]) |
| formatter.add_literal_data(" " + category) |
| spacing_width = 30 - len(category) |
| if category in qawarnings: |
| formatter.push_style("WARN") |
| else: |
| formatter.push_style("BAD") |
| formatter.add_literal_data(" [fatal]") |
| spacing_width -= 8 |
| |
| formatter.add_literal_data(" " * spacing_width) |
| formatter.add_literal_data("%s" % number) |
| formatter.pop_style() |
| formatter.add_line_break() |
| if not dofull: |
| if not full and dofail and category in qawarnings: |
| # warnings are considered noise when there are failures |
| continue |
| fails_list = fails[category] |
| if not full and len(fails_list) > 12: |
| fails_list = fails_list[:12] |
| for failure in fails_list: |
| formatter.add_literal_data(" " + failure) |
| formatter.add_line_break() |
| |
| |
| def format_qa_output_column( |
| formatter, fails, dofull, dofail, options, qawarnings): |
| """Helper function that formats output in a machine-parseable column format |
| |
| @param formatter: an instance of Formatter |
| @type formatter: Formatter |
| @param fails: dict of qa status failures |
| @type fails: dict |
| @param dofull: Whether to print full results or a summary |
| @type dofull: boolean |
| @param dofail: Whether failure was hard or soft |
| @type dofail: boolean |
| @param options: The command-line options provided to repoman |
| @type options: Namespace |
| @param qawarnings: the set of warning types |
| @type qawarnings: set |
| @return: None (modifies formatter) |
| """ |
| full = options.mode == 'full' |
| for category in sorted(fails): |
| number = len(fails[category]) |
| formatter.add_literal_data("NumberOf " + category + " ") |
| if category in qawarnings: |
| formatter.push_style("WARN") |
| else: |
| formatter.push_style("BAD") |
| formatter.add_literal_data("%s" % number) |
| formatter.pop_style() |
| formatter.add_line_break() |
| if not dofull: |
| if not full and dofail and category in qawarnings: |
| # warnings are considered noise when there are failures |
| continue |
| fails_list = fails[category] |
| if not full and len(fails_list) > 12: |
| fails_list = fails_list[:12] |
| for failure in fails_list: |
| formatter.add_literal_data(category + " " + failure) |
| formatter.add_line_break() |