From 9614067d58f80a40319abc941fdb663a33da1d4b Mon Sep 17 00:00:00 2001 From: Sorin Sbarnea Date: Sun, 18 Sep 2022 16:25:14 +0100 Subject: [PATCH] Improve noqa comment detection (#2440) In addition to fixing known bug, this also makes galaxy version error specify the correct line number. Fixes: #2382 --- examples/galaxy.yml | 2 +- src/ansiblelint/errors.py | 2 ++ src/ansiblelint/rules/galaxy.py | 6 ++++-- src/ansiblelint/runner.py | 4 +++- src/ansiblelint/skip_utils.py | 30 +++++++++++++++++++++++++----- 5 files changed, 35 insertions(+), 9 deletions(-) diff --git a/examples/galaxy.yml b/examples/galaxy.yml index f5cda0a9e8..55f314ff5f 100644 --- a/examples/galaxy.yml +++ b/examples/galaxy.yml @@ -1,7 +1,7 @@ --- name: foo namespace: bar -version: 1.2.3 +version: 0.0.0 # noqa: galaxy[version-incorrect] authors: - John readme: ../README.md diff --git a/src/ansiblelint/errors.py b/src/ansiblelint/errors.py index 72c0ffcf11..bc5a8c1d44 100644 --- a/src/ansiblelint/errors.py +++ b/src/ansiblelint/errors.py @@ -65,9 +65,11 @@ def __init__( self.filename = "" if filename: if isinstance(filename, Lintable): + self.lintable = filename self.filename = normpath(str(filename.path)) else: self.filename = normpath(filename) + self.lintable = Lintable(self.filename) self.rule = rule self.ignored = False # If set it will be displayed but not counted as failure # This can be used by rules that can report multiple errors type, so diff --git a/src/ansiblelint/rules/galaxy.py b/src/ansiblelint/rules/galaxy.py index 99145f0026..d436b62df2 100644 --- a/src/ansiblelint/rules/galaxy.py +++ b/src/ansiblelint/rules/galaxy.py @@ -38,11 +38,13 @@ def matchplay(self, file: Lintable, data: odict[str, Any]) -> list[MatchError]: filename=file, ) ] - if Version(data.get("version")) < Version("1.0.0"): + version = data.get("version") + if Version(version) < Version("1.0.0"): return [ self.create_matcherror( message="collection version should be greater than or equal to 1.0.0", - linenumber=data[LINE_NUMBER_KEY], + # pylint: disable=protected-access + linenumber=version._line_number, tag="galaxy[version-incorrect]", filename=file, ) diff --git a/src/ansiblelint/runner.py b/src/ansiblelint/runner.py index 97cf773a9c..0f5049c022 100644 --- a/src/ansiblelint/runner.py +++ b/src/ansiblelint/runner.py @@ -180,7 +180,9 @@ def worker(lintable: Lintable) -> list[MatchError]: # remove any matches made inside excluded files matches = list( filter( - lambda match: not self.is_excluded(Lintable(match.filename)), matches + lambda match: not self.is_excluded(Lintable(match.filename)) + and match.tag not in match.lintable.line_skips[match.linenumber], + matches, ) ) diff --git a/src/ansiblelint/skip_utils.py b/src/ansiblelint/skip_utils.py index 929dc53ca5..7617294a1b 100644 --- a/src/ansiblelint/skip_utils.py +++ b/src/ansiblelint/skip_utils.py @@ -30,6 +30,7 @@ # Module 'ruamel.yaml' does not explicitly export attribute 'YAML'; implicit reexport disabled from ruamel.yaml import YAML from ruamel.yaml.composer import ComposerError +from ruamel.yaml.tokens import CommentToken from ansiblelint.config import used_old_tags from ansiblelint.constants import NESTED_TASK_KEYS, PLAYBOOK_TASK_KEYWORDS, RENAMED_TAGS @@ -119,9 +120,17 @@ def _append_skipped_rules( # noqa: max-complexity: 12 ) -> AnsibleBaseYAMLObject | None: # parse file text using 2nd parser library ruamel_data = load_data(lintable.content) - skipped_rules = _get_rule_skips_from_yaml(ruamel_data) - - if lintable.kind in ["yaml", "requirements", "vars", "meta", "reno", "test-meta"]: + skipped_rules = _get_rule_skips_from_yaml(ruamel_data, lintable) + + if lintable.kind in [ + "yaml", + "requirements", + "vars", + "meta", + "reno", + "test-meta", + "galaxy", + ]: # AnsibleMapping, dict if hasattr(pyyaml_data, "get"): pyyaml_data["skipped_rules"] = skipped_rules @@ -166,7 +175,7 @@ def _append_skipped_rules( # noqa: max-complexity: 12 if pyyaml_task.get("name") != ruamel_task.get("name"): raise RuntimeError("Error in matching skip comment to a task") - pyyaml_task["skipped_rules"] = _get_rule_skips_from_yaml(ruamel_task) + pyyaml_task["skipped_rules"] = _get_rule_skips_from_yaml(ruamel_task, lintable) return pyyaml_data @@ -205,7 +214,7 @@ def get_nested_tasks(task: Any) -> Generator[Any, None, None]: def _get_rule_skips_from_yaml( # noqa: max-complexity: 12 - yaml_input: Sequence[Any], + yaml_input: Sequence[Any], lintable: Lintable ) -> Sequence[Any]: """Traverse yaml for comments with rule skips and return list of rules.""" yaml_comment_obj_strings = [] @@ -214,6 +223,17 @@ def _get_rule_skips_from_yaml( # noqa: max-complexity: 12 return [] def traverse_yaml(obj: Any) -> None: + for _, entry in obj.ca.items.items(): + for v in entry: + if isinstance(v, CommentToken): + comment_str = v.value + if comment_str.startswith("# noqa:"): + line = v.start_mark.line + 1 # ruamel line numbers start at 0 + # column = v.start_mark.column + 1 # ruamel column numbers start at 0 + lintable.line_skips[line].update( + get_rule_skips_from_line(comment_str.strip()) + ) + yaml_comment_obj_strings.append(str(obj.ca.items)) if isinstance(obj, dict): for _, val in obj.items():