| #!/usr/bin/env python |
| # -*- coding:utf-8 -*- |
| # Copyright 1999-2021 Gentoo Authors |
| # Distributed under the terms of the GNU General Public License v2 |
| |
| import collections |
| import io |
| import logging |
| import sys |
| |
| # import our centrally initialized portage instance |
| from repoman._portage import portage |
| |
| portage._internal_caller = True |
| portage._disable_legacy_globals() |
| |
| |
| from portage import os |
| import portage.checksum |
| import portage.const |
| import portage.repository.config |
| from portage.output import create_color_func, nocolor |
| from portage.output import ConsoleStyleFile, StyleWriter |
| from portage.util import formatter |
| from portage.util.futures.extendedfutures import ( |
| ExtendedFuture, |
| ) |
| |
| # pylint: disable=ungrouped-imports |
| from repoman.actions import Actions |
| from repoman.argparser import parse_args |
| from repoman.qa_data import QAData |
| from repoman.qa_data import format_qa_output, format_qa_output_column |
| from repoman.repos import RepoSettings |
| from repoman.scanner import Scanner |
| from repoman import utilities |
| from repoman.modules.vcs.settings import VCSSettings |
| from repoman import VERSION |
| |
| |
| bad = create_color_func("BAD") |
| |
| # A sane umask is needed for files that portage creates. |
| os.umask(0o22) |
| |
| LOGLEVEL = logging.WARNING |
| portage.util.initialize_logger(LOGLEVEL) |
| |
| VALID_VERSIONS = [ |
| 1, |
| ] |
| |
| _repoman_main_vars = collections.namedtuple( |
| "_repoman_main_vars", |
| ( |
| "can_force", |
| "exitcode", |
| "options", |
| "qadata", |
| "repo_settings", |
| "scanner", |
| "vcs_settings", |
| ), |
| ) |
| |
| |
| def repoman_main(argv): |
| repoman_vars = _repoman_init(argv) |
| if repoman_vars.exitcode is not None: |
| return repoman_vars.exitcode |
| result = _repoman_scan(*repoman_vars) |
| return _handle_result(*repoman_vars, result) |
| |
| |
| def _repoman_init(argv): |
| config_root = os.environ.get("PORTAGE_CONFIGROOT") |
| repoman_settings = portage.config(config_root=config_root, local_config=False) |
| repoman_settings.valid_versions = VALID_VERSIONS |
| |
| if ( |
| repoman_settings.get("NOCOLOR", "").lower() in ("yes", "true") |
| or repoman_settings.get("TERM") == "dumb" |
| or not sys.stdout.isatty() |
| ): |
| nocolor() |
| |
| options, arguments = parse_args( |
| argv, repoman_settings.get("REPOMAN_DEFAULT_OPTS", "") |
| ) |
| |
| if options.version: |
| print("Repoman", VERSION, "(portage-%s)" % portage.VERSION) |
| return _repoman_main_vars(None, 0, None, None, None, None, None) |
| |
| logger = logging.getLogger() |
| |
| if options.verbosity > 0: |
| logger.setLevel(LOGLEVEL - 10 * options.verbosity) |
| else: |
| logger.setLevel(LOGLEVEL) |
| |
| # Set this to False when an extraordinary issue (generally |
| # something other than a QA issue) makes it impossible to |
| # commit (like if Manifest generation fails). |
| can_force = ExtendedFuture(True) |
| repo_settings, vcs_settings, scanner, qadata = _create_scanner( |
| options, can_force, config_root, repoman_settings |
| ) |
| return _repoman_main_vars( |
| can_force, None, options, qadata, repo_settings, scanner, vcs_settings |
| ) |
| |
| |
| def _create_scanner(options, can_force, config_root, repoman_settings): |
| |
| portdir, portdir_overlay, mydir = utilities.FindPortdir(repoman_settings) |
| if portdir is None: |
| return (None, None, None, None) |
| |
| myreporoot = os.path.basename(portdir_overlay) |
| myreporoot += mydir[len(portdir_overlay) :] |
| |
| # avoid a circular parameter repo_settings |
| vcs_settings = VCSSettings(options, repoman_settings) |
| qadata = QAData() |
| |
| logging.debug("repoman_main: RepoSettings init") |
| repo_settings = RepoSettings( |
| config_root, |
| portdir, |
| portdir_overlay, |
| repoman_settings, |
| vcs_settings, |
| options, |
| qadata, |
| ) |
| repoman_settings = repo_settings.repoman_settings |
| repoman_settings.valid_versions = VALID_VERSIONS |
| |
| # Now set repo_settings |
| vcs_settings.repo_settings = repo_settings |
| # set QATracker qacats, qawarnings |
| vcs_settings.qatracker.qacats = repo_settings.qadata.qacats |
| vcs_settings.qatracker.qawarnings = repo_settings.qadata.qawarnings |
| logging.debug("repoman_main: vcs_settings done") |
| logging.debug("repoman_main: qadata: %s", repo_settings.qadata) |
| |
| if "digest" in repoman_settings.features and options.digest != "n": |
| options.digest = "y" |
| |
| logging.debug("vcs: %s" % (vcs_settings.vcs,)) |
| logging.debug("repo config: %s" % (repo_settings.repo_config,)) |
| logging.debug("options: %s" % (options,)) |
| |
| # It's confusing if these warnings are displayed without the user |
| # being told which profile they come from, so disable them. |
| env = os.environ.copy() |
| env["FEATURES"] = env.get("FEATURES", "") + " -unknown-features-warn" |
| |
| # Perform the main checks |
| scanner = Scanner( |
| repo_settings, myreporoot, config_root, options, vcs_settings, mydir, env |
| ) |
| return repo_settings, vcs_settings, scanner, qadata |
| |
| |
| def _repoman_scan( |
| can_force, exitcode, options, qadata, repo_settings, scanner, vcs_settings |
| ): |
| scanner.scan_pkgs(can_force) |
| |
| if options.if_modified == "y" and len(scanner.effective_scanlist) < 1: |
| logging.warning( |
| "--if-modified is enabled, but no modified packages were found!" |
| ) |
| |
| result = { |
| # fail will be true if we have failed in at least one non-warning category |
| "fail": 0, |
| # warn will be true if we tripped any warnings |
| "warn": 0, |
| # full will be true if we should print a "repoman full" informational message |
| "full": options.mode != "full", |
| } |
| |
| for x in qadata.qacats: |
| if x not in vcs_settings.qatracker.fails: |
| continue |
| result["warn"] = 1 |
| if x not in qadata.qawarnings: |
| result["fail"] = 1 |
| |
| if result["fail"] or ( |
| result["warn"] and not (options.quiet or options.mode == "scan") |
| ): |
| result["full"] = 0 |
| |
| return result |
| |
| |
| def _handle_result( |
| can_force, exitcode, options, qadata, repo_settings, scanner, vcs_settings, result |
| ): |
| commitmessage = None |
| if options.commitmsg: |
| commitmessage = options.commitmsg |
| elif options.commitmsgfile: |
| # we don't need the actual text of the commit message here |
| # the filename will do for the next code block |
| commitmessage = options.commitmsgfile |
| |
| # Save QA output so that it can be conveniently displayed |
| # in $EDITOR while the user creates a commit message. |
| # Otherwise, the user would not be able to see this output |
| # once the editor has taken over the screen. |
| qa_output = io.StringIO() |
| style_file = ConsoleStyleFile(sys.stdout) |
| if options.mode == "commit" and (not commitmessage or not commitmessage.strip()): |
| style_file.write_listener = qa_output |
| console_writer = StyleWriter(file=style_file, maxcol=9999) |
| console_writer.style_listener = style_file.new_styles |
| |
| f = formatter.AbstractFormatter(console_writer) |
| |
| format_outputs = {"column": format_qa_output_column, "default": format_qa_output} |
| |
| format_output = format_outputs.get(options.output_style, format_outputs["default"]) |
| format_output( |
| f, |
| vcs_settings.qatracker.fails, |
| result["full"], |
| result["fail"], |
| options, |
| qadata.qawarnings, |
| ) |
| |
| style_file.flush() |
| del console_writer, f, style_file |
| |
| # early out for manifest generation |
| if options.mode == "manifest": |
| return 1 if result["fail"] else 0 |
| |
| qa_output = qa_output.getvalue() |
| qa_output = qa_output.splitlines(True) |
| |
| # output the results |
| actions = Actions(repo_settings, options, scanner, vcs_settings) |
| if actions.inform(can_force.get(), result): |
| # perform any other actions |
| actions.perform(qa_output) |
| elif result["fail"]: |
| return 1 |
| |
| return 0 |