Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
b9d7b0b
make rule deprecated-local-action preserve module parameters
koalajoe23 Aug 17, 2025
89d92a0
Add test cases to fixtures
koalajoe23 Aug 17, 2025
0cbbc0a
run rule test in isolation and do not relying on default ruleset side…
koalajoe23 Aug 17, 2025
1efee58
refactored the formatter to please SonarCloud (hopefully)
koalajoe23 Aug 17, 2025
71de621
Add type hint to please mypy
koalajoe23 Aug 17, 2025
a07ebbf
Merge branch 'main' into fix_deprecated_local_action
cidrblock Aug 17, 2025
80b0e93
Downgrade log statements for malformed ansible to asserts
koalajoe23 Aug 17, 2025
263035f
Move typing.Any outside typing.TYPE_CHECKING
koalajoe23 Aug 18, 2025
6e99842
Make handling of parameterless modules more verbose w/ fixture
koalajoe23 Aug 18, 2025
25ea6b5
Fix indentation in docs correct code example
koalajoe23 Aug 18, 2025
e2960af
Examples
cidrblock Aug 19, 2025
3443218
Delete GITHUB_ACTION_LOCK_ISSUE.md
cidrblock Aug 19, 2025
ab8ab63
rm comment
cidrblock Aug 19, 2025
78ba533
fixes
cidrblock Aug 19, 2025
1d75be7
Update settings.json
cidrblock Aug 19, 2025
30acbc9
Update settings.json
cidrblock Aug 19, 2025
d8fa092
bad vulture
cidrblock Aug 19, 2025
a62aa99
parametrize
cidrblock Aug 19, 2025
34612e1
empty dict
cidrblock Aug 20, 2025
a57e9bd
chore: auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 20, 2025
7a430b1
rm breakpoint
cidrblock Aug 20, 2025
47b2403
ergh line
cidrblock Aug 20, 2025
06b2464
ergh line
cidrblock Aug 20, 2025
7032c8d
hide empty dicts from transform output again
koalajoe23 Aug 20, 2025
baca5b6
Merge branch 'main' into fix_deprecated_local_action
alisonlhart Aug 21, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions examples/playbooks/tasks/local_action.transformed.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
---
- name: Sample
ansible.builtin.command: echo 123
- name: Sample 1
command: echo 123
delegate_to: localhost
- name: Sample 2
command:
cmd: echo 456
delegate_to: localhost
- name: Sample 3
ansible.builtin.debug:
delegate_to: localhost
8 changes: 7 additions & 1 deletion examples/playbooks/tasks/local_action.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
---
- name: Sample
- name: Sample 1
local_action: command echo 123
- name: Sample 2
local_action:
module: command
cmd: echo 456
- name: Sample 3
local_action: ansible.builtin.debug
2 changes: 1 addition & 1 deletion src/ansiblelint/rules/deprecated_local_action.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ This rule recommends using `delegate_to: localhost` instead of the

```yaml
- name: Task example
ansible.builtin.debug:
ansible.builtin.debug:
delegate_to: localhost # <-- recommended way to run on localhost
```

Expand Down
37 changes: 23 additions & 14 deletions src/ansiblelint/rules/deprecated_local_action.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import os
import sys
from pathlib import Path
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Any

from ansiblelint.rules import AnsibleLintRule, TransformMixin
from ansiblelint.runner import get_matches
Expand Down Expand Up @@ -53,19 +53,27 @@ def transform(
data: CommentedMap | CommentedSeq | str,
) -> None:
if match.tag == self.id:
# we do not want perform a partial modification accidentally
original_target_task = self.seek(match.yaml_path, data)
target_task = copy.deepcopy(original_target_task)
for _ in range(len(target_task)):
k, v = target_task.popitem(False)
assert "local_action" in original_target_task
target_task: dict[str, Any] = {}

for k, v in original_target_task.items():
if k == "local_action":
if isinstance(v, dict):
module_name = v["module"]
target_task[module_name] = None
assert "module" in v
target_task[v["module"]] = copy.deepcopy(v)
target_task[v["module"]].pop("module", None)

if target_task[v["module"]] == {}:
target_task[v["module"]] = None

target_task["delegate_to"] = "localhost"
elif isinstance(v, str):
module_name, module_value = v.split(" ", 1)
target_task[module_name] = module_value
tokens = v.split(" ", 1)
if len(tokens) > 1:
target_task[tokens[0]] = tokens[1]
else:
target_task[tokens[0]] = None
target_task["delegate_to"] = "localhost"
else: # pragma: no cover
_logger.debug(
Expand All @@ -75,6 +83,7 @@ def transform(
return
else:
target_task[k] = v

match.fixed = True
original_target_task.clear()
original_target_task.update(target_task)
Expand All @@ -94,27 +103,27 @@ def test_local_action(default_rules_collection: RulesCollection) -> None:
rules=default_rules_collection,
).run()

assert len(results) == 1
assert results[0].tag == "deprecated-local-action"
assert any(result.tag == "deprecated-local-action" for result in results)

@mock.patch.dict(os.environ, {"ANSIBLE_LINT_WRITE_TMP": "1"}, clear=True)
def test_local_action_transform(
config_options: Options,
default_rules_collection: RulesCollection,
) -> None:
"""Test transform functionality for no-log-password rule."""
playbook = Path("examples/playbooks/tasks/local_action.yml")
config_options.write_list = ["all"]

config_options.lintables = [str(playbook)]
only_local_action_rule: RulesCollection = RulesCollection()
only_local_action_rule.register(TaskNoLocalActionRule())
runner_result = get_matches(
rules=default_rules_collection,
rules=only_local_action_rule,
options=config_options,
)
transformer = Transformer(result=runner_result, options=config_options)
transformer.run()
matches = runner_result.matches
assert len(matches) == 3
assert any(error.tag == "deprecated-local-action" for error in matches)

orig_content = playbook.read_text(encoding="utf-8")
expected_content = playbook.with_suffix(
Expand Down
Loading