Skip to content

Commit d0162aa

Browse files
authored
Fix transform exception with local_action with old syntax (#3743)
1 parent 3f38702 commit d0162aa

File tree

4 files changed

+69
-5
lines changed

4 files changed

+69
-5
lines changed

.github/workflows/tox.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ jobs:
7171
WSLENV: FORCE_COLOR:PYTEST_REQPASS:TOXENV:GITHUB_STEP_SUMMARY
7272
# Number of expected test passes, safety measure for accidental skip of
7373
# tests. Update value if you add/remove tests.
74-
PYTEST_REQPASS: 823
74+
PYTEST_REQPASS: 824
7575
steps:
7676
- name: Activate WSL1
7777
if: "contains(matrix.shell, 'wsl')"
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
- name: Sample
3+
ansible.builtin.command: echo 123
4+
delegate_to: localhost
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
---
2+
- name: Sample
3+
local_action: command echo 123

src/ansiblelint/rules/deprecated_local_action.py

Lines changed: 61 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,29 @@
33
# Copyright (c) 2018, Ansible Project
44
from __future__ import annotations
55

6+
import copy
7+
import logging
68
import sys
79
from typing import TYPE_CHECKING
810

911
from ansiblelint.rules import AnsibleLintRule, TransformMixin
12+
from ansiblelint.runner import _get_matches
13+
from ansiblelint.transformer import Transformer
1014

1115
if TYPE_CHECKING:
16+
from pathlib import Path
17+
1218
from ruamel.yaml.comments import CommentedMap, CommentedSeq
1319

20+
from ansiblelint.config import Options
1421
from ansiblelint.errors import MatchError
1522
from ansiblelint.file_utils import Lintable
1623
from ansiblelint.utils import Task
1724

1825

26+
_logger = logging.getLogger(__name__)
27+
28+
1929
class TaskNoLocalAction(AnsibleLintRule, TransformMixin):
2030
"""Do not use 'local_action', use 'delegate_to: localhost'."""
2131

@@ -45,16 +55,31 @@ def transform(
4555
data: CommentedMap | CommentedSeq | str,
4656
) -> None:
4757
if match.tag == self.id:
48-
target_task = self.seek(match.yaml_path, data)
58+
# we do not want perform a partial modification accidentally
59+
original_target_task = self.seek(match.yaml_path, data)
60+
target_task = copy.deepcopy(original_target_task)
4961
for _ in range(len(target_task)):
5062
k, v = target_task.popitem(False)
5163
if k == "local_action":
52-
module_name = v["module"]
53-
target_task[module_name] = None
54-
target_task["delegate_to"] = "localhost"
64+
if isinstance(v, dict):
65+
module_name = v["module"]
66+
target_task[module_name] = None
67+
target_task["delegate_to"] = "localhost"
68+
elif isinstance(v, str):
69+
module_name, module_value = v.split(" ", 1)
70+
target_task[module_name] = module_value
71+
target_task["delegate_to"] = "localhost"
72+
else:
73+
_logger.debug(
74+
"Ignored unexpected data inside %s transform.",
75+
self.id,
76+
)
77+
return
5578
else:
5679
target_task[k] = v
5780
match.fixed = True
81+
original_target_task.clear()
82+
original_target_task.update(target_task)
5883

5984

6085
# testing code to be loaded only with pytest or when executed the rule file
@@ -71,3 +96,35 @@ def test_local_action(default_rules_collection: RulesCollection) -> None:
7196

7297
assert len(results) == 1
7398
assert results[0].tag == "deprecated-local-action"
99+
100+
def test_local_action_transform(
101+
config_options: Options,
102+
copy_examples_dir: tuple[Path, Path],
103+
default_rules_collection: RulesCollection,
104+
) -> None:
105+
"""Test transform functionality for no-log-password rule."""
106+
playbook: str = "examples/playbooks/tasks/local_action.yml"
107+
config_options.write_list = ["all"]
108+
109+
config_options.lintables = [playbook]
110+
runner_result = _get_matches(
111+
rules=default_rules_collection,
112+
options=config_options,
113+
)
114+
transformer = Transformer(result=runner_result, options=config_options)
115+
transformer.run()
116+
117+
matches = runner_result.matches
118+
assert len(matches) == 3
119+
120+
orig_dir, tmp_dir = copy_examples_dir
121+
orig_playbook = orig_dir / playbook
122+
expected_playbook = orig_dir / playbook.replace(".yml", ".transformed.yml")
123+
transformed_playbook = tmp_dir / playbook
124+
125+
orig_playbook_content = orig_playbook.read_text()
126+
expected_playbook_content = expected_playbook.read_text()
127+
transformed_playbook_content = transformed_playbook.read_text()
128+
129+
assert orig_playbook_content != transformed_playbook_content
130+
assert transformed_playbook_content == expected_playbook_content

0 commit comments

Comments
 (0)