diff --git a/.github/workflows/tox.yml b/.github/workflows/tox.yml index c37aa89820..af7510a0f0 100644 --- a/.github/workflows/tox.yml +++ b/.github/workflows/tox.yml @@ -134,7 +134,7 @@ jobs: - name: Install tox run: | python3 -m pip install --upgrade pip - python3 -m pip install --upgrade "tox>=4.0.0" "tox-uv>=1.16.0" uv + python3 -m pip install --upgrade "tox>=4.0.0" "tox-uv>=1.20.2" uv - name: Log installed dists run: python3 -m pip freeze --all diff --git a/src/ansiblelint/file_utils.py b/src/ansiblelint/file_utils.py index 6b22168185..715851729b 100644 --- a/src/ansiblelint/file_utils.py +++ b/src/ansiblelint/file_utils.py @@ -407,7 +407,8 @@ def is_owned_by_ansible(self) -> bool: def failed(self) -> bool: """Return true if we already found syntax-check errors on this file.""" return any( - match.rule.id in ("syntax-check", "load-failure") for match in self.matches + match.rule.id in ("syntax-check", "load-failure", "internal-error") + for match in self.matches ) @property diff --git a/src/ansiblelint/rules/__init__.py b/src/ansiblelint/rules/__init__.py index 9c1f9a5349..c9807c1deb 100644 --- a/src/ansiblelint/rules/__init__.py +++ b/src/ansiblelint/rules/__init__.py @@ -241,8 +241,8 @@ def matchyaml(self, file: Lintable) -> list[MatchError]: yaml = [yaml] for play in yaml: - # Bug #849 - if play is None: + # Bug #849 and #4492 + if play is None or not hasattr(play, "get"): continue if self.id in play.get(SKIPPED_RULES_KEY, ()): diff --git a/src/ansiblelint/runner.py b/src/ansiblelint/runner.py index 2390d25875..76fbd8c8e0 100644 --- a/src/ansiblelint/runner.py +++ b/src/ansiblelint/runner.py @@ -270,10 +270,12 @@ def worker(lintable: Lintable) -> list[MatchError]: # do our processing only when ansible syntax check passed in order # to avoid causing runtime exceptions. Our processing is not as # resilient to be able process garbage. - matches.extend(self._emit_matches(files)) + matches.extend( + self._emit_matches([file for file in files if not file.failed()]) + ) # remove duplicates from files list - files = [value for n, value in enumerate(files) if value not in files[:n]] + files = list(dict.fromkeys(files)) for file in self.lintables: if file in self.checked_files or not file.kind or file.failed(): @@ -433,6 +435,7 @@ def _get_ansible_syntax_check_matches( f"Unexpected error code {run.returncode} from " f"execution of: {' '.join(cmd)}" ) + filename.failed() results.append( MatchError( message=message, @@ -462,6 +465,8 @@ def _emit_matches(self, files: list[Lintable]) -> Generator[MatchError, None, No while visited != self.lintables: for lintable in self.lintables - visited: visited.add(lintable) + if lintable.failed(): + continue if not lintable.path.exists(): continue try: @@ -499,9 +504,12 @@ def find_children(self, lintable: Lintable) -> list[Lintable]: try: playbook_ds = ansiblelint.utils.parse_yaml_from_file(str(lintable.path)) except AnsibleError as exc: - msg = f"Loading {lintable.filename} caused an {type(exc).__name__} exception: {exc}, file was ignored." - _logger.exception(msg) - return [] + raise MatchError( + lintable=lintable, rule=self.rules["load-failure"] + ) from exc + # msg = f"Loading {lintable.filename} caused an {type(exc).__name__} exception: {exc}, file was ignored." + # _logger.exception(msg) + # return [] results = [] # playbook_ds can be an AnsibleUnicode string, which we consider invalid if isinstance(playbook_ds, str): diff --git a/src/ansiblelint/skip_utils.py b/src/ansiblelint/skip_utils.py index 0cb33d1648..5c54ec1aae 100644 --- a/src/ansiblelint/skip_utils.py +++ b/src/ansiblelint/skip_utils.py @@ -316,5 +316,8 @@ def is_nested_task(task: dict[str, Any]) -> bool: # Cannot really trust the input if isinstance(task, str): return False + # https://github.com/ansible/ansible-lint/issues/4492 + if not hasattr(task, "get"): + return False return any(task.get(key) for key in NESTED_TASK_KEYS)