From 84818c7116ff4e7ecbbf1375a0af0d572df8021b Mon Sep 17 00:00:00 2001 From: Sorin Sbarnea Date: Mon, 13 May 2024 20:08:31 +0100 Subject: [PATCH] Allow running with incompatible yamllint config Fixes: #4152 --- .pre-commit-config.yaml | 4 ++-- src/ansiblelint/__main__.py | 8 ++++++++ src/ansiblelint/app.py | 5 +++++ src/ansiblelint/yaml_utils.py | 27 +++++++++++++++++++-------- test/test_yaml_utils.py | 9 +++------ 5 files changed, 37 insertions(+), 16 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2bbed8a5fa..5ffbf17962 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -162,8 +162,8 @@ repos: - pytest-mock - pytest>=7.2.2 - rich>=13.2.0 - - ruamel-yaml-clib>=0.2.7 - - ruamel-yaml>=0.18.2 + - ruamel-yaml-clib>=0.2.8 + - ruamel-yaml>=0.18.6 - subprocess-tee - types-PyYAML - types-jsonschema>=4.20.0.0 diff --git a/src/ansiblelint/__main__.py b/src/ansiblelint/__main__.py index 8b6fd4348d..ca4a33b65c 100755 --- a/src/ansiblelint/__main__.py +++ b/src/ansiblelint/__main__.py @@ -373,7 +373,15 @@ def main(argv: list[str] | None = None) -> int: match.ignored = True _logger.debug("Ignored: %s", match) + if app.yamllint_config.incompatible: + logging.log( + level=logging.ERROR if options.write_list else logging.WARNING, + msg=app.yamllint_config.incompatible, + ) + if options.write_list: + if app.yamllint_config.incompatible: + sys.exit(RC.INVALID_CONFIG) fix(runtime_options=options, result=result, rules=rules) app.render_matches(result.matches) diff --git a/src/ansiblelint/app.py b/src/ansiblelint/app.py index 0d319cfa99..71171e61e5 100644 --- a/src/ansiblelint/app.py +++ b/src/ansiblelint/app.py @@ -54,6 +54,11 @@ def __init__(self, options: Options): verbosity=options.verbosity, ) + # pylint: disable=import-outside-toplevel + from ansiblelint.yaml_utils import load_yamllint_config # noqa: 811,I001 + + self.yamllint_config = load_yamllint_config() + def render_matches(self, matches: list[MatchError]) -> None: """Display given matches (if they are not fixed).""" matches = [match for match in matches if not match.fixed] diff --git a/src/ansiblelint/yaml_utils.py b/src/ansiblelint/yaml_utils.py index 1b2a67c6a1..6a479211be 100644 --- a/src/ansiblelint/yaml_utils.py +++ b/src/ansiblelint/yaml_utils.py @@ -7,7 +7,6 @@ import logging import os import re -import sys from collections.abc import Callable, Iterator, Sequence from io import StringIO from pathlib import Path @@ -31,7 +30,6 @@ ANNOTATION_KEYS, NESTED_TASK_KEYS, PLAYBOOK_TASK_KEYWORDS, - RC, ) from ansiblelint.utils import Task @@ -48,6 +46,19 @@ _logger = logging.getLogger(__name__) +class CustomYamlLintConfig(YamlLintConfig): # type: ignore[misc] + """Extension of YamlLintConfig.""" + + def __init__( + self, + content: str | None = None, + file: str | Path | None = None, + ) -> None: + """Initialize config.""" + super().__init__(content, file) + self.incompatible = "" + + def deannotate(data: Any) -> Any: """Remove our annotations like __file__ and __line__ and return a JSON serializable object.""" if isinstance(data, dict): @@ -63,9 +74,10 @@ def deannotate(data: Any) -> Any: return data -def load_yamllint_config() -> YamlLintConfig: +def load_yamllint_config() -> CustomYamlLintConfig: """Load our default yamllint config and any customized override file.""" - config = YamlLintConfig(file=Path(__file__).parent / "data" / ".yamllint") + config = CustomYamlLintConfig(file=Path(__file__).parent / "data" / ".yamllint") + config.incompatible = "" # if we detect local yamllint config we use it but raise a warning # as this is likely to get out of sync with our internal config. for path in [ @@ -82,7 +94,7 @@ def load_yamllint_config() -> YamlLintConfig: "internal yamllint config.", file, ) - custom_config = YamlLintConfig(file=str(file)) + custom_config = CustomYamlLintConfig(file=str(file)) custom_config.extend(config) config = custom_config break @@ -138,9 +150,8 @@ def load_yamllint_config() -> YamlLintConfig: errors.append(msg) if errors: nl = "\n" - msg = f"Found incompatible custom yamllint configuration ({file}), please either remove the file or edit it to comply with:{nl} - {nl + ' - '.join(errors)}.{nl}{nl}Read https://ansible.readthedocs.io/projects/lint/rules/yaml/ for more details regarding why we have these requirements." - logging.fatal(msg) - sys.exit(RC.INVALID_CONFIG) + msg = f"Found incompatible custom yamllint configuration ({file}), please either remove the file or edit it to comply with:{nl} - {(nl + ' - ').join(errors)}.{nl}{nl}Read https://ansible.readthedocs.io/projects/lint/rules/yaml/ for more details regarding why we have these requirements. Fix mode will not be available." + config.incompatible = msg _logger.debug("Effective yamllint rules used: %s", config.rules) return config diff --git a/test/test_yaml_utils.py b/test/test_yaml_utils.py index f885cf47c6..e2f6f4f684 100644 --- a/test/test_yaml_utils.py +++ b/test/test_yaml_utils.py @@ -12,7 +12,6 @@ from yamllint.linter import run as run_yamllint import ansiblelint.yaml_utils -from ansiblelint.constants import RC from ansiblelint.file_utils import Lintable, cwd from ansiblelint.utils import task_in_list @@ -996,8 +995,6 @@ def test_deannotate( def test_yamllint_incompatible_config() -> None: """Ensure we can detect incompatible yamllint settings.""" - with ( - cwd(Path("examples/yamllint/incompatible-config")), - pytest.raises(SystemExit, match=f"^{RC.INVALID_CONFIG}$"), - ): - ansiblelint.yaml_utils.load_yamllint_config() + with (cwd(Path("examples/yamllint/incompatible-config")),): + config = ansiblelint.yaml_utils.load_yamllint_config() + assert config.incompatible