summaryrefslogtreecommitdiffstats
path: root/src/ansiblelint/transformer.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/ansiblelint/transformer.py')
-rw-r--r--src/ansiblelint/transformer.py50
1 files changed, 40 insertions, 10 deletions
diff --git a/src/ansiblelint/transformer.py b/src/ansiblelint/transformer.py
index 3716ef9..c610704 100644
--- a/src/ansiblelint/transformer.py
+++ b/src/ansiblelint/transformer.py
@@ -1,8 +1,10 @@
+# cspell:ignore classinfo
"""Transformer implementation."""
+
from __future__ import annotations
import logging
-from typing import TYPE_CHECKING, Union, cast
+from typing import TYPE_CHECKING, cast
from ruamel.yaml.comments import CommentedMap, CommentedSeq
@@ -20,7 +22,6 @@ __all__ = ["Transformer"]
_logger = logging.getLogger(__name__)
-# pylint: disable=too-few-public-methods
class Transformer:
"""Transformer class marshals transformations.
@@ -33,6 +34,17 @@ class Transformer:
pre-requisite for the planned rule-specific transforms.
"""
+ DUMP_MSG = "Rewriting yaml file:"
+ FIX_NA_MSG = "Rule specific fix not available for:"
+ FIX_NE_MSG = "Rule specific fix not enabled for:"
+ FIX_APPLY_MSG = "Applying rule specific fix for:"
+ FIX_FAILED_MSG = "Rule specific fix failed for:"
+ FIX_ISSUE_MSG = (
+ "Please file an issue for this with the task or playbook that caused the error."
+ )
+ FIX_APPLIED_MSG = "Rule specific fix applied for:"
+ FIX_NOT_APPLIED_MSG = "Rule specific fix not applied for:"
+
def __init__(self, result: LintResult, options: Options):
"""Initialize a Transformer instance."""
self.write_set = self.effective_write_set(options.write_list)
@@ -44,8 +56,8 @@ class Transformer:
self.matches_per_file: dict[Lintable, list[MatchError]] = {
file: [] for file in result.files
}
-
- for match in self.matches:
+ not_ignored = [match for match in self.matches if not match.ignored]
+ for match in not_ignored:
try:
lintable = lintables[match.filename]
except KeyError:
@@ -93,10 +105,13 @@ class Transformer:
# We need a fresh YAML() instance for each load because ruamel.yaml
# stores intermediate state during load which could affect loading
# any other files. (Based on suggestion from ruamel.yaml author)
- yaml = FormattedYAML()
+ yaml = FormattedYAML(
+ # Ansible only uses YAML 1.1, but others files should use newer 1.2 (ruamel.yaml defaults to 1.2)
+ version=(1, 1) if file.is_owned_by_ansible() else None,
+ )
- ruamel_data = yaml.loads(data)
- if not isinstance(ruamel_data, (CommentedMap, CommentedSeq)):
+ ruamel_data = yaml.load(data)
+ if not isinstance(ruamel_data, CommentedMap | CommentedSeq):
# This is an empty vars file or similar which loads as None.
# It is not safe to write this file or data-loss is likely.
# Only maps and sequences can preserve comments. Skip it.
@@ -110,6 +125,7 @@ class Transformer:
self._do_transforms(file, ruamel_data or data, file_is_yaml, matches)
if file_is_yaml:
+ _logger.debug("%s %s, version=%s", self.DUMP_MSG, file, yaml.version)
# noinspection PyUnboundLocalVariable
file.content = yaml.dumps(ruamel_data)
@@ -125,17 +141,19 @@ class Transformer:
) -> None:
"""Do Rule-Transforms handling any last-minute MatchError inspections."""
for match in sorted(matches):
+ match_id = f"{match.tag}/{match.match_type} {match.filename}:{match.lineno}"
if not isinstance(match.rule, TransformMixin):
+ logging.debug("%s %s", self.FIX_NA_MSG, match_id)
continue
if self.write_set != {"all"}:
rule = cast(AnsibleLintRule, match.rule)
rule_definition = set(rule.tags)
rule_definition.add(rule.id)
if rule_definition.isdisjoint(self.write_set):
- # rule transform not requested. Skip it.
+ logging.debug("%s %s", self.FIX_NE_MSG, match_id)
continue
if file_is_yaml and not match.yaml_path:
- data = cast(Union[CommentedMap, CommentedSeq], data)
+ data = cast(CommentedMap | CommentedSeq, data)
if match.match_type == "play":
match.yaml_path = get_path_to_play(file, match.lineno, data)
elif match.task or file.kind in (
@@ -144,4 +162,16 @@ class Transformer:
"playbook",
):
match.yaml_path = get_path_to_task(file, match.lineno, data)
- match.rule.transform(match, file, data)
+
+ logging.debug("%s %s", self.FIX_APPLY_MSG, match_id)
+ try:
+ match.rule.transform(match, file, data)
+ except Exception as exc: # pylint: disable=broad-except
+ _logger.error("%s %s", self.FIX_FAILED_MSG, match_id) # noqa: TRY400
+ _logger.exception(exc) # noqa: TRY401
+ _logger.error(self.FIX_ISSUE_MSG) # noqa: TRY400
+ continue
+ if match.fixed:
+ _logger.debug("%s %s", self.FIX_APPLIED_MSG, match_id)
+ else:
+ _logger.error("%s %s", self.FIX_NOT_APPLIED_MSG, match_id)