Ejemplo n.º 1
0
import sys
import tempfile
from typing import TYPE_CHECKING, Any, Dict, List, Optional

from ansiblelint.prerun import prepare_environment

if TYPE_CHECKING:
    # https://github.com/PyCQA/pylint/issues/3240
    # pylint: disable=unsubscriptable-object
    CompletedProcess = subprocess.CompletedProcess[Any]
else:
    CompletedProcess = subprocess.CompletedProcess

# Emulate command line execution initialization as without it Ansible module
# would be loaded with incomplete module/role/collection list.
prepare_environment()

# pylint: disable=wrong-import-position
from ansiblelint.errors import MatchError  # noqa: E402
from ansiblelint.rules import RulesCollection  # noqa: E402
from ansiblelint.runner import Runner  # noqa: E402


class RunFromText:
    """Use Runner on temp files created from unittest text snippets."""
    def __init__(self, collection: RulesCollection) -> None:
        """Initialize a RunFromText instance with rules collection."""
        self.collection = collection

    def _call_runner(self, path: str) -> List["MatchError"]:
        runner = Runner(path, rules=self.collection)
Ejemplo n.º 2
0
def main(argv: Optional[List[str]] = None) -> int:
    """Linter CLI entry point."""
    if argv is None:
        argv = sys.argv
    initialize_options(argv[1:])

    console_options["force_terminal"] = options.colored
    reconfigure(console_options)

    initialize_logger(options.verbosity)
    _logger.debug("Options: %s", options)

    app = App(options=options)

    prepare_environment()
    check_ansible_presence(exit_on_error=True)

    # On purpose lazy-imports to avoid pre-loading Ansible
    # pylint: disable=import-outside-toplevel
    from ansiblelint.generate_docs import rules_as_rich, rules_as_rst, rules_as_str
    from ansiblelint.rules import RulesCollection

    rules = RulesCollection(options.rulesdirs)

    if options.listrules:

        _rule_format_map: Dict[str, Callable[..., Any]] = {
            'plain': rules_as_str,
            'rich': rules_as_rich,
            'rst': rules_as_rst,
        }

        console.print(_rule_format_map[options.format](rules), highlight=False)
        return 0

    if options.listtags:
        console.print(render_yaml(rules.listtags()))
        return 0

    if isinstance(options.tags, str):
        options.tags = options.tags.split(',')

    from ansiblelint.runner import _get_matches

    result = _get_matches(rules, options)

    mark_as_success = False
    if result.matches and options.progressive:
        _logger.info(
            "Matches found, running again on previous revision in order to detect regressions"
        )
        with _previous_revision():
            old_result = _get_matches(rules, options)
            # remove old matches from current list
            matches_delta = list(set(result.matches) - set(old_result.matches))
            if len(matches_delta) == 0:
                _logger.warning(
                    "Total violations not increased since previous "
                    "commit, will mark result as success. (%s -> %s)",
                    len(old_result.matches),
                    len(matches_delta),
                )
                mark_as_success = True

            ignored = 0
            for match in result.matches:
                # if match is not new, mark is as ignored
                if match not in matches_delta:
                    match.ignored = True
                    ignored += 1
            if ignored:
                _logger.warning(
                    "Marked %s previously known violation(s) as ignored due to"
                    " progressive mode.",
                    ignored,
                )

    app.render_matches(result.matches)

    return report_outcome(result,
                          mark_as_success=mark_as_success,
                          options=options)
Ejemplo n.º 3
0
def execute_cmdline_scenarios(scenario_name, args, command_args, ansible_args=()):
    """
    Execute scenario sequences based on parsed command-line arguments.

    This is useful for subcommands that run scenario sequences, which
    excludes subcommands such as ``list``, ``login``, and ``matrix``.

    ``args`` and ``command_args`` are combined using :func:`get_configs`
    to generate the scenario(s) configuration.

    :param scenario_name: Name of scenario to run, or ``None`` to run all.
    :param args: ``args`` dict from ``click`` command context
    :param command_args: dict of command arguments, including the target
                         subcommand to execute
    :returns: None

    """
    glob_str = MOLECULE_GLOB
    if scenario_name:
        glob_str = glob_str.replace("*", scenario_name)
    scenarios = molecule.scenarios.Scenarios(
        get_configs(args, command_args, ansible_args, glob_str), scenario_name
    )

    if scenario_name and scenarios:
        LOG.info(
            "%s scenario test matrix: %s",
            scenario_name,
            ", ".join(scenarios.sequence(scenario_name)),
        )

    for scenario in scenarios:

        if scenario.config.config["prerun"]:
            LOG.info("Performing prerun...")
            prepare_environment()

        if command_args.get("subcommand") == "reset":
            LOG.info("Removing %s" % scenario.ephemeral_directory)
            shutil.rmtree(scenario.ephemeral_directory)
            return
        try:
            execute_scenario(scenario)
        except SystemExit:
            # if the command has a 'destroy' arg, like test does,
            # handle that behavior here.
            if command_args.get("destroy") == "always":
                msg = (
                    "An error occurred during the {} sequence action: "
                    "'{}'. Cleaning up."
                ).format(scenario.config.subcommand, scenario.config.action)
                LOG.warning(msg)
                execute_subcommand(scenario.config, "cleanup")
                execute_subcommand(scenario.config, "destroy")
                # always prune ephemeral dir if destroying on failure
                scenario.prune()
                if scenario.config.is_parallel:
                    scenario._remove_scenario_state_directory()
                util.sysexit()
            else:
                raise