From e1bf4522af97b4c704578c26ca783eb52e5e934a Mon Sep 17 00:00:00 2001 From: Jimmy Berry Date: Wed, 2 Aug 2017 21:17:28 -0500 Subject: [PATCH] repo_checker: provide optional parsing of install check output and mapping to package. --- repo_checker.py | 49 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/repo_checker.py b/repo_checker.py index 932537b3..1c1f5bd7 100755 --- a/repo_checker.py +++ b/repo_checker.py @@ -3,12 +3,14 @@ from collections import namedtuple import os import pipes +import re import subprocess import sys import tempfile from osclib.core import binary_list from osclib.core import depends_on +from osclib.core import package_binary_list from osclib.core import request_staged from osclib.core import target_archs from osclib.cycle import CycleDetector @@ -18,6 +20,8 @@ import ReviewBot SCRIPT_PATH = os.path.dirname(os.path.realpath(__file__)) CheckResult = namedtuple('CheckResult', ('success', 'comment')) +INSTALL_REGEX = r"^(?:can't install (.*?)|found conflict of (.*?) with (.*?)):$" +InstallSection = namedtuple('InstallSection', ('binaries', 'text')) class RepoChecker(ReviewBot.ReviewBot): def __init__(self, *args, **kwargs): @@ -59,6 +63,9 @@ class RepoChecker(ReviewBot.ReviewBot): self.group = None self.mirrored = set() + # Stores parsed install_check() results grouped by package. + self.package_results = {} + # Look for requests of interest and group by staging. for request in self.requests: # Only interesting if request is staged. @@ -208,7 +215,7 @@ class RepoChecker(ReviewBot.ReviewBot): whitelist.update(self.staging_config[project].get(key, '').split(' ')) return whitelist - def install_check(self, directory_project, directory_group, arch, ignore, whitelist): + def install_check(self, directory_project, directory_group, arch, ignore, whitelist, parse=False): self.logger.info('install check: start') with tempfile.NamedTemporaryFile() as ignore_file: @@ -233,6 +240,10 @@ class RepoChecker(ReviewBot.ReviewBot): if p.returncode == 126: self.logger.warn('mirror cache reset due to corruption') self.mirrored = set() + elif parse: + # Parse output for later consumption for posting comments. + sections = self.install_check_parse(stdout) + self.install_check_sections_group(parse, arch, sections) # Format output as markdown comment. code = '```\n' @@ -251,6 +262,42 @@ class RepoChecker(ReviewBot.ReviewBot): self.logger.info('install check: passed') return CheckResult(True, None) + def install_check_sections_group(self, project, arch, sections): + _, binary_map = package_binary_list(self.apiurl, project, 'standard', arch) + + for section in sections: + # If switch to creating bugs likely makes sense to join packages to + # form grouping key and create shared bugs for conflicts. + packages = set([binary_map[b] for b in section.binaries]) + for package in packages: + self.package_results.setdefault(package, []) + self.package_results[package].append(section) + + def install_check_parse(self, output): + section = None + text = None + + # Loop over lines and parse into chunks assigned to binaries. + for line in output.splitlines(True): + if line.startswith(' '): + if section: + text += line + else: + if section: + yield InstallSection(section, text) + + match = re.match(INSTALL_REGEX, line) + if match: + # Remove empty groups since regex matches different patterns. + binaries = [b for b in match.groups() if b is not None] + section = binaries + text = line + else: + section = None + + if section: + yield InstallSection(section, text) + def cycle_check(self, project, group, arch): if self.skip_cycle: self.logger.info('cycle check: skip due to --skip-cycle')