Skip to content

Commit 30bb3cc

Browse files
authored
Add name[play] to highlight plays without name (#2295)
From now on, we will be require plays to have names, same as task, as these are also displayed by Ansible and help documenting them. Related: #2171
1 parent d9cc363 commit 30bb3cc

File tree

8 files changed

+79
-25
lines changed

8 files changed

+79
-25
lines changed

.github/workflows/tox.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ jobs:
165165
WSLENV: FORCE_COLOR:PYTEST_REQPASS:TOXENV:TOX_PARALLEL_NO_SPINNER
166166
# Number of expected test passes, safety measure for accidental skip of
167167
# tests. Update value if you add/remove tests.
168-
PYTEST_REQPASS: 693
168+
PYTEST_REQPASS: 694
169169

170170
steps:
171171
- name: Activate WSL1
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
---
2+
- hosts: localhost # <-- name[missing]
3+
tasks: []
Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
---
2-
- hosts: all
2+
- hosts: all # <-- name[missing]
33
tasks:
4-
- ansible.builtin.command: echo "no name" # <-- 1
4+
- ansible.builtin.command: echo "no name" # <-- name[missing]
55
changed_when: false
6-
- name: "" # <-- 2
6+
- name: "" # <-- name[missing]
77
ansible.builtin.command: echo "empty name"
88
changed_when: false
9-
- ansible.builtin.debug: # <-- 3
9+
- ansible.builtin.debug: # <-- name[missing]
1010
msg: Debug without a name
11-
- ansible.builtin.meta: flush_handlers # <-- 4
11+
- ansible.builtin.meta: flush_handlers # <-- name[missing]

src/ansiblelint/config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
from ansiblelint.loaders import yaml_from_file
1212

13-
DEFAULT_WARN_LIST = ["experimental", "name[casing]", "role-name"]
13+
DEFAULT_WARN_LIST = ["experimental", "name[casing]", "name[play]", "role-name"]
1414

1515
DEFAULT_KINDS = [
1616
# Do not sort this list, order matters.

src/ansiblelint/rules/meta_no_tags.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ def matchyaml(self, file: Lintable) -> list[MatchError]:
105105
def test_valid_tag_rule(rule_runner: Any) -> None:
106106
"""Test rule matches."""
107107
results = rule_runner.run_role_meta_main(META_TAG_VALID)
108-
assert "Use 'galaxy_tags' rather than 'categories'" in str(results)
108+
assert "Use 'galaxy_tags' rather than 'categories'" in str(results), results
109109
assert "Expected 'categories' to be a list" in str(results)
110110
assert "invalid: 'my s q l'" in str(results)
111111
assert "invalid: 'MYTAG'" in str(results)

src/ansiblelint/rules/name.md

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,32 @@
11
## name
22

3-
This rule identifies several problems related to task naming. This is important
4-
because these names are the primary way to identify executed tasks on console,
5-
logs or web interface. Their role is also to document what a task is supposed
6-
to do.
3+
This rule identifies several problems related to naming of tasks and plays.
4+
This is important because these names are the primary way to identify executed
5+
operations on console, logs or web interface. Their role is also to document
6+
what Ansible is supposed to do.
77

8-
- `name[missing]` - All tasks are required to have a name
9-
- `name[casing]` - All task names should start with a capital letter
8+
This rule can produce messages such:
9+
10+
- `name[casing]` - All names should start with an uppercase letter.
11+
- `name[missing]` - All tasks should be named.
12+
- `name[play]` - All plays should be named.
1013

1114
### Problematic code
1215

1316
```yaml
1417
---
15-
- ansible.builtin.command: touch /tmp/.placeholder
18+
- hosts: localhost
19+
tasks:
20+
- ansible.builtin.command: touch /tmp/.placeholder
1621
```
1722
1823
### Correct code
1924
2025
```yaml
2126
---
22-
- name: Create placeholder file
23-
ansible.builtin.command: touch /tmp/.placeholder
27+
- name: Play for creating playholder
28+
hosts: localhost
29+
tasks:
30+
- name: Create placeholder file
31+
ansible.builtin.command: touch /tmp/.placeholder
2432
```

src/ansiblelint/rules/name.py

Lines changed: 49 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,44 @@
1111
if TYPE_CHECKING:
1212
from typing import Optional
1313

14+
from ansiblelint.constants import odict
1415
from ansiblelint.file_utils import Lintable
1516

1617

1718
class NameRule(AnsibleLintRule):
18-
"""Rule for checking task names and their content."""
19+
"""Rule for checking task and play names."""
1920

2021
id = "name"
2122
description = (
22-
"All tasks should have a distinct name for readability "
23+
"All tasks and plays should have a distinct name for readability "
2324
"and for ``--start-at-task`` to work"
2425
)
2526
severity = "MEDIUM"
2627
tags = ["idiom"]
27-
version_added = "historic"
28+
version_added = "v6.5.0 (last update)"
29+
30+
def matchplay(self, file: Lintable, data: odict[str, Any]) -> list[MatchError]:
31+
"""Return matches found for a specific play (entry in playbook)."""
32+
if file.kind != "playbook":
33+
return []
34+
if "name" not in data and not any(
35+
key in data
36+
for key in ["import_playbook", "ansible.builtin.import_playbook"]
37+
):
38+
return [
39+
self.create_matcherror(
40+
message="All plays should be named.",
41+
linenumber=data[LINE_NUMBER_KEY],
42+
tag="name[play]",
43+
filename=file,
44+
)
45+
]
46+
match = self._check_name(
47+
data["name"], lintable=file, linenumber=data[LINE_NUMBER_KEY]
48+
)
49+
if match:
50+
return [match]
51+
return []
2852

2953
def matchtask(
3054
self, task: dict[str, Any], file: Lintable | None = None
@@ -37,14 +61,22 @@ def matchtask(
3761
tag="name[missing]",
3862
filename=file,
3963
)
64+
return (
65+
self._check_name(name, lintable=file, linenumber=task[LINE_NUMBER_KEY])
66+
or False
67+
)
68+
69+
def _check_name(
70+
self, name: str, lintable: Lintable | None, linenumber: int
71+
) -> MatchError | None:
4072
if not name[0].isupper():
4173
return self.create_matcherror(
4274
message="All names should start with an uppercase letter.",
43-
linenumber=task[LINE_NUMBER_KEY],
75+
linenumber=linenumber,
4476
tag="name[casing]",
45-
filename=file,
77+
filename=lintable,
4678
)
47-
return False
79+
return None
4880

4981

5082
if "pytest" in sys.modules: # noqa: C901
@@ -67,7 +99,7 @@ def test_file_negative() -> None:
6799
failure = "examples/playbooks/task-has-name-failure.yml"
68100
bad_runner = Runner(failure, rules=collection)
69101
errs = bad_runner.run()
70-
assert len(errs) == 4
102+
assert len(errs) == 5
71103

72104
def test_rule_name_lowercase() -> None:
73105
"""Negative test for a task that starts with lowercase."""
@@ -79,3 +111,13 @@ def test_rule_name_lowercase() -> None:
79111
assert len(errs) == 1
80112
assert errs[0].tag == "name[casing]"
81113
assert errs[0].rule.id == "name"
114+
115+
def test_name_play() -> None:
116+
"""Positive test for name[play]."""
117+
collection = RulesCollection()
118+
collection.register(NameRule())
119+
success = "examples/playbooks/play-name-missing.yml"
120+
errs = Runner(success, rules=collection).run()
121+
assert len(errs) == 1
122+
assert errs[0].tag == "name[play]"
123+
assert errs[0].rule.id == "name"

test/test_skiputils.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@
1919

2020
PLAYBOOK_WITH_NOQA = """\
2121
---
22-
- hosts: all
22+
- name: Fixture
23+
hosts: all
2324
vars:
2425
SOME_VAR_NOQA: "Foo" # noqa var-naming
2526
SOME_VAR: "Bar"

0 commit comments

Comments
 (0)